001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.planning.plugin.util;
028:
029: import java.util.ArrayList;
030: import java.util.Arrays;
031: import java.util.Enumeration;
032: import java.util.Iterator;
033: import java.util.List;
034:
035: import org.cougaar.planning.ldm.plan.AllocationResult;
036: import org.cougaar.planning.ldm.plan.AspectRate;
037: import org.cougaar.planning.ldm.plan.AspectType;
038: import org.cougaar.planning.ldm.plan.AspectValue;
039: import org.cougaar.planning.ldm.plan.PlanElement;
040: import org.cougaar.planning.ldm.plan.Preference;
041: import org.cougaar.planning.ldm.plan.ScoringFunction;
042: import org.cougaar.planning.ldm.plan.Task;
043: import org.cougaar.planning.ldm.plan.TimeAspectValue;
044:
045: /**
046: * Manages AllocationResults having phased results representing
047: * varying quantities and rates over time.
048: **/
049: public class AllocationResultHelper {
050: public class Phase {
051: public AspectValue[] result;
052:
053: private Phase(int ix) {
054: result = (AspectValue[]) phasedResults.get(ix);
055: }
056:
057: public AspectValue getAspectValue(int type) {
058: return result[getTypeIndex(type)];
059: }
060:
061: public long getStartTime() {
062: if (startix < 0)
063: throw new RuntimeException("No START_TIME for " + task);
064: return (long) result[startix].getValue();
065: }
066:
067: public long getEndTime() {
068: if (endix < 0)
069: throw new RuntimeException("No END_TIME for " + task);
070: return (long) result[endix].getValue();
071: }
072:
073: public double getQuantity() {
074: if (qtyix < 0)
075: throw new RuntimeException("No QUANTITY for " + task);
076: return result[qtyix].getValue();
077: }
078: }
079:
080: /** The Task whose disposition we are computing a result. **/
081: private Task task;
082:
083: /** The index into the arrays of the START_TIME aspect **/
084: private int startix = -1;
085:
086: /** The index into the arrays of the END_TIME aspect **/
087: private int endix = -1;
088:
089: /** The index into the arrays of the QUANTITY aspect **/
090: private int qtyix = -1;
091:
092: /** The new perfectResult **/
093: private AspectValue[] perfectResult;
094:
095: /** The new phased results **/
096: private List phasedResults = new ArrayList();
097:
098: /** The AspectType map **/
099: private int[] typeMap;
100:
101: /** Has this allocation result been changed **/
102: private boolean isChanged = false;
103:
104: private AllocationResult ar;
105:
106: public AllocationResultHelper(Task task, PlanElement pe) {
107: AspectValue[] taskAVS = getAspectValuesOfTask(task);
108: this .task = task;
109: ar = null;
110: if (pe != null) {
111: ar = pe.getEstimatedResult();
112: }
113: if (ar != null) {
114: if (ar.isPhased()) {
115: phasedResults = ar.getPhasedAspectValueResults();
116: } else {
117: phasedResults = new ArrayList(1);
118: phasedResults.add(ar.getAspectValueResults());
119: }
120: setTypeIndexes((AspectValue[]) phasedResults.get(0));
121: // checkPhases(phasedResults);
122: } else {
123: phasedResults = new ArrayList(1);
124: phasedResults.add(taskAVS);
125: setTypeIndexes(taskAVS);
126: }
127: perfectResult = getPerfectResult(taskAVS);
128: }
129:
130: private AspectValue[] getAspectValuesOfTask(Task task) {
131: List avs = new ArrayList();
132: synchronized (task) {
133: for (Enumeration e = task.getPreferences(); e
134: .hasMoreElements();) {
135: Preference pref = (Preference) e.nextElement();
136: AspectValue best = pref.getScoringFunction().getBest()
137: .getAspectValue();
138: avs.add(best);
139: }
140: }
141: return (AspectValue[]) avs.toArray(new AspectValue[avs.size()]);
142: }
143:
144: private AspectValue[] getPerfectResult(AspectValue[] avs) {
145: AspectValue[] result = new AspectValue[avs.length];
146: result = AspectValue
147: .clone((AspectValue[]) phasedResults.get(0));
148: for (int i = 0; i < avs.length; i++) {
149: result[getTypeIndex(avs[i].getAspectType())] = avs[i];
150: }
151: return result;
152: }
153:
154: public AllocationResult getAllocationResult() {
155: return getAllocationResult(1.0);
156: }
157:
158: public AllocationResult getAllocationResult(double confrating) {
159: AspectValue[] ru = computeRollup();
160: return getAllocationResult(confrating, isSuccess(ru), ru);
161: }
162:
163: public AllocationResult getAllocationResult(double confrating,
164: boolean isSuccess) {
165: return getAllocationResult(confrating, isSuccess,
166: computeRollup());
167: }
168:
169: private AllocationResult getAllocationResult(double confrating,
170: boolean isSuccess, AspectValue[] ru) {
171: return new AllocationResult(confrating, isSuccess, ru,
172: phasedResults);
173: }
174:
175: private boolean isSuccess(AspectValue[] ru) {
176: int ix = 0;
177: synchronized (task) {
178: for (Enumeration e = task.getPreferences(); e
179: .hasMoreElements(); ix++) {
180: Preference pref = (Preference) e.nextElement();
181: ScoringFunction sf = pref.getScoringFunction();
182: AspectValue av = ru[ix];
183: double this Score = sf.getScore(av);
184: if (this Score >= ScoringFunction.HIGH_THRESHOLD)
185: return false;
186: }
187: }
188: return true;
189: }
190:
191: public boolean isChanged() {
192: return isChanged;
193: }
194:
195: private void checkPhases(List phasedResults) {
196: if (startix < 0 || endix < 0)
197: return;
198: for (int i = 0, n = phasedResults.size(); i < n; i++) {
199: AspectValue[] pi = (AspectValue[]) phasedResults.get(i);
200: long si = getStartTime(pi);
201: long ei = getEndTime(pi);
202: for (int j = i + 1; j < n; j++) {
203: AspectValue[] pj = (AspectValue[]) phasedResults.get(j);
204: long sj = getStartTime(pj);
205: long ej = getEndTime(pj);
206: if (sj >= ei || si >= ej)
207: continue;
208: System.err.println("Bad phases " + ar);
209: int p = 0;
210: for (Iterator it = phasedResults.iterator(); it
211: .hasNext();) {
212: System.err.println("Phase " + p);
213: AspectValue[] phase = (AspectValue[]) it.next();
214: for (int q = 0; q < phase.length; q++) {
215: System.err.println(" " + phase[q]);
216: }
217: }
218: Thread.dumpStack();
219: System.exit(1);
220: }
221: }
222: }
223:
224: public int getPhaseCount() {
225: // checkPhases(phasedResults);
226: return phasedResults.size();
227: }
228:
229: public Phase getPhase(int i) {
230: return new Phase(i);
231: }
232:
233: /**
234: * Set a successful value over a period of time
235: **/
236: public void setBest(int type, long startTime, long endTime) {
237: int ix = getTypeIndex(type);
238: Preference pref = task.getPreference(type);
239: AspectValue av = pref.getScoringFunction().getBest()
240: .getAspectValue();
241: set(ix, av, startTime, endTime);
242: }
243:
244: /**
245: * Set a failed value over a period of time. New phased results
246: * are edited into the results as needed.
247: **/
248: public void setFailed(int type, long startTime, long endTime) {
249: int ix = getTypeIndex(type);
250: AspectValue av = perfectResult[ix].dupAspectValue(0.0);
251: set(ix, av, startTime, endTime);
252: }
253:
254: private int getTypeIndex(int type) {
255: if (type < 0 || typeMap == null || type >= typeMap.length) {
256: throw new IllegalArgumentException("Type " + type
257: + " not found");
258: }
259: return typeMap[type];
260: }
261:
262: /**
263: * Edit the exiting results to reflect a particular value of the
264: * indicated aspect over a given time period. Find existing
265: * segments with different values that overlap the new segment and
266: * adjust their times to not overlap. Then try to combine the new
267: * segment with existing results having the same value and
268: * adjacent or overlapping times. Finally, if the new segment
269: * cannot be combined with any existing segment, add a new
270: * segment. This does not fix the rollup result since that depends
271: * on what aspect is edited.
272: * @param valueix the index in the arrays of the aspect to change
273: * @param value the new value for the time period
274: * @param startTime the time when the value starts to apply
275: * @param endTime the time when the value no longer applies.
276: * @return true if a change was made.
277: **/
278: List newResults = null;
279:
280: private void set(int valueix, AspectValue av, long s, long e) {
281: long startTime = s;
282: long endTime = e;
283: if (newResults == null)
284: newResults = new ArrayList(phasedResults.size() + 2); // At most two new results
285: boolean covered = false;
286: boolean this Changed = false;
287: AspectValue[] newResult;
288: long minTime = getStartTime(perfectResult);
289: long maxTime = getEndTime(perfectResult);
290: if (minTime < maxTime) {
291: /* Only process if there is overlap between the arguments and
292: the start/end time aspects of the perfectResult **/
293: if (startTime >= maxTime) {
294: return; // Does not apply
295: }
296: endTime = Math.min(endTime, maxTime);
297: if (endTime <= minTime) {
298: return; // Does not apply
299: }
300: startTime = Math.max(startTime, minTime);
301:
302: for (Iterator i = phasedResults.iterator(); i.hasNext();) {
303: AspectValue[] oneResult = (AspectValue[]) i.next();
304: long this Start = getStartTime(oneResult);
305: long this End = getEndTime(oneResult);
306: AspectValue this Value = oneResult[valueix];
307: if (this Value.equals(av)) { // Maybe combine these
308: newResult = AspectValue.clone(oneResult);
309: if (startTime <= this End && endTime >= this Start) { // Overlaps
310: if (this Start < startTime)
311: startTime = this Start;
312: if (this End > endTime)
313: endTime = this End;
314: this Changed = true;
315: continue;
316: } else {
317: newResults.add(newResult);
318: }
319: } else {
320: if (startTime < this End && endTime > this Start) { // Overlaps
321: if (startTime > this Start) { // Initial portion exists
322: newResult = AspectValue.clone(oneResult);
323: newResult[endix] = TimeAspectValue.create(
324: AspectType.END_TIME, startTime);
325: newResults.add(newResult);
326: }
327: if (endTime < this End) { // Final portion exists
328: newResult = AspectValue.clone(oneResult);
329: newResult[startix] = TimeAspectValue
330: .create(AspectType.START_TIME,
331: endTime);
332: newResults.add(newResult);
333: }
334: this Changed = true;
335: } else {
336: newResult = AspectValue.clone(oneResult);
337: newResults.add(newResult);
338: }
339: }
340: }
341: } else {
342: if (startTime > minTime || endTime <= minTime)
343: return;
344: if (perfectResult[valueix].equals(av))
345: return;
346: newResult = AspectValue.clone(perfectResult);
347: newResult[valueix] = av;
348: newResults.add(newResult);
349: this Changed = true;
350: covered = true;
351: }
352: if (!covered) {
353: newResult = AspectValue.clone(perfectResult);
354: newResult[startix] = TimeAspectValue.create(
355: AspectType.START_TIME, startTime);
356: newResult[endix] = TimeAspectValue.create(
357: AspectType.END_TIME, endTime);
358: newResult[valueix] = av;
359: newResults.add(newResult);
360: this Changed = true;
361: }
362: if (!this Changed) {
363: return; // No changes were made
364: }
365: isChanged = true;
366: // checkPhases(newResults);
367: phasedResults.clear();
368: phasedResults.addAll(newResults);
369: newResults.clear();
370: }
371:
372: private AspectValue[] computeRollup() {
373: double[] sums = new double[perfectResult.length];
374: double[] divisor = new double[perfectResult.length];
375: boolean first = true;
376: Arrays.fill(sums, 0.0);
377: Arrays.fill(divisor, 1.0);
378: for (Iterator iter = phasedResults.iterator(); iter.hasNext();) {
379: AspectValue[] oneResult = (AspectValue[]) iter.next();
380: for (int i = 0; i < oneResult.length; i++) {
381: AspectValue av = oneResult[i];
382: double v = av.getValue();
383: int type = av.getAspectType();
384: boolean doAverage = (av instanceof AspectRate || type > AspectType._LAST_ASPECT);
385: if (first) {
386: sums[i] = v;
387: } else {
388: switch (type) {
389: default:
390: if (doAverage)
391: divisor[i] += 1.0;
392: sums[i] += v;
393: break;
394: case AspectType.START_TIME:
395: sums[i] = Math.min(sums[i], v);
396: break;
397: case AspectType.END_TIME:
398: sums[i] = Math.max(sums[i], v);
399: break;
400: }
401: }
402: }
403: first = false;
404: }
405: AspectValue[] ru = AspectValue.clone(perfectResult);
406: for (int i = 0; i < ru.length; i++) {
407: ru[i] = ru[i].dupAspectValue(sums[i] / divisor[i]);
408: }
409: return ru;
410: }
411:
412: private long getStartTime(AspectValue[] avs) {
413: return (long) avs[(startix >= 0) ? startix : endix].getValue();
414: }
415:
416: private long getEndTime(AspectValue[] avs) {
417: return (long) avs[(endix >= 0) ? endix : startix].getValue();
418: }
419:
420: private void setTypeIndexes(AspectValue[] avs) {
421: int maxIndex = AspectType._ASPECT_COUNT;
422: for (int i = 0; i < avs.length; i++) {
423: maxIndex = Math.max(maxIndex, avs[i].getAspectType());
424: }
425: typeMap = new int[maxIndex + 1];
426: Arrays.fill(typeMap, -1);
427: for (int i = 0; i < avs.length; i++) {
428: int type = avs[i].getAspectType();
429: typeMap[type] = i;
430: }
431: startix = getTypeIndex(AspectType.START_TIME);
432: endix = getTypeIndex(AspectType.END_TIME);
433: qtyix = getTypeIndex(AspectType.QUANTITY);
434: }
435: }
|