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