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.lib.vishnu.client;
028:
029: import org.cougaar.planning.ldm.plan.AllocationResult;
030: import org.cougaar.planning.ldm.plan.AllocationResultAggregator;
031: import org.cougaar.planning.ldm.plan.AspectType;
032: import org.cougaar.planning.ldm.plan.AuxiliaryQueryType;
033: import org.cougaar.planning.ldm.plan.Task;
034: import org.cougaar.planning.ldm.plan.TaskScoreTable;
035: import org.cougaar.planning.ldm.plan.Verb;
036: import org.cougaar.planning.ldm.plan.Workflow;
037:
038: import java.io.Serializable;
039:
040: /**
041: * VishnuAllocationResultAggregator is a class which specifies how AllocationResults
042: * should be aggregated. Currently called by Workflow.aggregateAllocationResults. <p>
043: *
044: * Largely a copy of the default AllocationResultAggregator, except that it doesn't
045: * use the time aspects of tasks with Verb TRANSIT. These are for setup and wrapup tasks
046: * that are only indirectly related to the parent task.
047: *
048: * @see org.cougaar.lib.vishnu.client.VishnuPlugin#makeSetupWrapupExpansion
049: * @see org.cougaar.planning.ldm.plan.Workflow#aggregateAllocationResults
050: * @see org.cougaar.planning.ldm.plan.AllocationResult
051: **/
052:
053: public class VishnuAllocationResultAggregator implements
054: AllocationResultAggregator, Serializable, AspectType // for Constants
055: {
056: double SIGNIFICANT_CONFIDENCE_RATING_DELTA = 0.0001;
057:
058: /** Does the right computation for workflows which are made up of
059: * equally important tasks with no inter-task constraints.
060: * START_TIME is minimized.
061: * END_TIME is maximized.
062: * DURATION is overall END_TIME - overall START_TIME.
063: * COST is summed.
064: * DANGER is maximized.
065: * RISK is maximized.
066: * QUANTITY is summed.
067: * INTERVAL is summed.
068: * TOTAL_QUANTITY is summed.
069: * TOTAL_SHIPMENTS is summed.
070: * CUSTOMER_SATISFACTION is averaged.
071: * READINESS is minimized.
072: * Any extended aspect types are ignored.
073: *
074: * For AuxiliaryQuery information, if all the query values are the same
075: * across subtasks or one subtask has query info it will be place in the
076: * aggregate result. However, if there are conflicting query values, no
077: * information will be put in the aggregated result.
078: *
079: * returns null when there are no subtasks or any task has no result.
080: **/
081: private static final String UNDEFINED = "UNDEFINED";
082:
083: public AllocationResult calculate(Workflow wf, TaskScoreTable tst,
084: AllocationResult currentar) {
085: double acc[] = new double[AspectType._ASPECT_COUNT];
086: acc[START_TIME] = Double.MAX_VALUE;
087: acc[END_TIME] = 0.0;
088: // duration is computed from end values of start and end
089: acc[COST] = 0.0;
090: acc[DANGER] = 0.0;
091: acc[RISK] = 0.0;
092: acc[QUANTITY] = 0.0;
093: acc[INTERVAL] = 0.0;
094: acc[TOTAL_QUANTITY] = 0.0;
095: acc[TOTAL_SHIPMENTS] = 0.0;
096: acc[CUSTOMER_SATISFACTION] = 1.0; // start at best
097: acc[READINESS] = 1.0;
098:
099: boolean ap[] = new boolean[AspectType._ASPECT_COUNT];
100:
101: boolean suc = true;
102: double rating = 0.0;
103:
104: if (tst == null)
105: return null;
106: int tstSize = tst.size();
107: if (tstSize == 0)
108: return null;
109: int totalSkipped = 0;
110:
111: String auxqsummary[] = new String[AuxiliaryQueryType.AQTYPE_COUNT];
112: // initialize all values to UNDEFINED for comparison purposes below.
113: int aql = auxqsummary.length;
114: for (int aqs = 0; aqs < aql; aqs++) {
115: auxqsummary[aqs] = UNDEFINED;
116: }
117:
118: int hash = 0;
119: for (int i = 0; i < tstSize; i++) {
120: Task t = tst.getTask(i);
121: AllocationResult ar = tst.getAllocationResult(i);
122: if (ar == null)
123: return null; // bail if undefined
124:
125: // found a task with Transit verb -- ignore, since it's indirect
126: if (t.getVerb().equals(Verb.get("Transit"))) {
127: totalSkipped++;
128: continue;
129: }
130:
131: suc = suc && ar.isSuccess();
132: rating += ar.getConfidenceRating();
133:
134: int[] definedaspects = ar.getAspectTypes();
135: int al = definedaspects.length;
136: for (int b = 0; b < al; b++) {
137: // accumulate the values for the defined aspects
138: switch (definedaspects[b]) {
139: case START_TIME:
140: acc[START_TIME] = Math.min(acc[START_TIME], ar
141: .getValue(START_TIME));
142: ap[START_TIME] = true;
143: hash |= (1 << START_TIME);
144: break;
145: case END_TIME:
146: acc[END_TIME] = Math.max(acc[END_TIME], ar
147: .getValue(END_TIME));
148: ap[END_TIME] = true;
149: hash |= (1 << END_TIME);
150: break;
151: // compute duration later
152: case COST:
153: acc[COST] += ar.getValue(COST);
154: ap[COST] = true;
155: hash |= (1 << COST);
156: break;
157: case DANGER:
158: acc[DANGER] = Math.max(acc[DANGER], ar
159: .getValue(DANGER));
160: ap[DANGER] = true;
161: hash |= (1 << DANGER);
162: break;
163: case RISK:
164: acc[RISK] = Math.max(acc[RISK], ar.getValue(RISK));
165: ap[RISK] = true;
166: hash |= (1 << RISK);
167: break;
168: case QUANTITY:
169: acc[QUANTITY] += ar.getValue(QUANTITY);
170: ap[QUANTITY] = true;
171: hash |= (1 << QUANTITY);
172: break;
173: // for now simply add the repetitve task values
174: case INTERVAL:
175: acc[INTERVAL] += ar.getValue(INTERVAL);
176: ap[INTERVAL] = true;
177: hash |= (1 << INTERVAL);
178: break;
179: case TOTAL_QUANTITY:
180: acc[TOTAL_QUANTITY] += ar.getValue(TOTAL_QUANTITY);
181: ap[TOTAL_QUANTITY] = true;
182: hash |= (1 << TOTAL_QUANTITY);
183: break;
184: case TOTAL_SHIPMENTS:
185: acc[TOTAL_SHIPMENTS] += ar
186: .getValue(TOTAL_SHIPMENTS);
187: ap[TOTAL_SHIPMENTS] = true;
188: hash |= (1 << TOTAL_SHIPMENTS);
189: break;
190: //end of repetitive task specific aspects
191: case CUSTOMER_SATISFACTION:
192: acc[CUSTOMER_SATISFACTION] += ar
193: .getValue(CUSTOMER_SATISFACTION);
194: ap[CUSTOMER_SATISFACTION] = true;
195: hash |= (1 << CUSTOMER_SATISFACTION);
196: break;
197: case READINESS:
198: acc[READINESS] = Math.min(acc[READINESS], ar
199: .getValue(READINESS));
200: ap[READINESS] = true;
201: hash |= (1 << READINESS);
202: break;
203: }
204: }
205:
206: // Sum up the auxiliaryquery data. If there are conflicting data
207: // values, send back nothing for that type. If only one subtask
208: // has information about a querytype, send it back in the
209: // aggregated result.
210: for (int aq = 0; aq < AuxiliaryQueryType.AQTYPE_COUNT; aq++) {
211: String data = ar.auxiliaryQuery(aq);
212: if (data != null) {
213: String sumdata = auxqsummary[aq];
214: // if sumdata = null, there has already been a conflict.
215: if (sumdata != null) {
216: if (sumdata.equals(UNDEFINED)) {
217: // there's not a value yet, so use this one.
218: auxqsummary[aq] = data;
219: } else if (!data.equals(sumdata)) {
220: // there's a conflict, pass back null
221: auxqsummary[aq] = null;
222: }
223: }
224: }
225: }
226:
227: } // end of looping through all subtasks
228:
229: // compute duration IFF defined.
230: if (ap[START_TIME] && ap[END_TIME]) {
231: acc[DURATION] = acc[END_TIME] - acc[START_TIME];
232: ap[DURATION] = true;
233: hash |= (1 << DURATION);
234: } else {
235: // redundant
236: acc[DURATION] = 0.0;
237: ap[DURATION] = false;
238: }
239:
240: if (tstSize > 0) {
241: acc[CUSTOMER_SATISFACTION] /= (tstSize - totalSkipped);
242: rating /= (tstSize - totalSkipped);
243: }
244:
245: boolean delta = false;
246:
247: // only check the defined aspects and make sure that the currentar is not null
248: if (currentar == null) {
249: delta = true; // if the current ar == null then set delta true
250: } else {
251: int[] caraspects = currentar.getAspectTypes();
252: if (caraspects.length != acc.length) {
253: //if the current ar length is different than the length of the new
254: // calculations (acc) there's been a change
255: delta = true;
256: } else {
257: int il = caraspects.length;
258: for (int i = 0; i < il; i++) {
259: int da = caraspects[i];
260: if (ap[da] && acc[da] != currentar.getValue(da)) {
261: delta = true;
262: break;
263: }
264: }
265: }
266:
267: if (!delta) {
268: if (currentar.isSuccess() != suc) {
269: delta = true;
270: } else if (Math.abs(currentar.getConfidenceRating()
271: - rating) > SIGNIFICANT_CONFIDENCE_RATING_DELTA) {
272: delta = true;
273: }
274: }
275: }
276:
277: if (delta) {
278: int keys[] = _STANDARD_ASPECTS;
279: int al = AspectType._ASPECT_COUNT;
280:
281: // see if we should compress the results array
282: int lc = 0;
283: for (int b = 0; b < al; b++) {
284: if (ap[b])
285: lc++;
286: }
287:
288: if (lc < al) { // need to compress the arrays
289: double nv[] = new double[lc];
290:
291: // slow, big, general case
292: /*
293: {
294: int nk[] = new int[lc];
295: int i = 0;
296: for (int b = 0; b<al; b++) {
297: if (ap[b]) {
298: nv[i] = acc[b];
299: nk[i] = keys[b];
300: i++;
301: }
302: }
303: acc = nv;
304: keys = nk;
305: }
306: */
307:
308: // lazy cache the key patterns
309: synchronized (hack) {
310: Integer ihash = new Integer(hash);
311: KeyHolder kh = (KeyHolder) hack.get(ihash);
312: if (kh == null) {
313: //System.err.println("Caching key "+hash);
314: int nk[] = new int[lc];
315: int i = 0;
316: for (int b = 0; b < al; b++) {
317: if (ap[b]) {
318: nv[i] = acc[b];
319: nk[i] = keys[b];
320: i++;
321: }
322: }
323: acc = nv;
324: keys = nk;
325: kh = new KeyHolder(nk);
326: hack.put(ihash, kh);
327: } else {
328: keys = kh.keys;
329: int i = 0;
330: for (int b = 0; b < al; b++) {
331: if (ap[b]) {
332: nv[i] = acc[b];
333: i++;
334: }
335: }
336: acc = nv;
337: }
338: }
339:
340: }
341:
342: AllocationResult artoreturn = new AllocationResult(rating,
343: suc, keys, acc);
344:
345: int aqll = auxqsummary.length;
346: for (int aqt = 0; aqt < aql; aqt++) {
347: String aqdata = auxqsummary[aqt];
348: if ((aqdata != null) && (aqdata != UNDEFINED)) {
349: artoreturn.addAuxiliaryQueryInfo(aqt, aqdata);
350: }
351: }
352: return artoreturn;
353: } else {
354: return currentar;
355: }
356: }
357: }
|