0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 1997-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026:
0027: package org.cougaar.planning.ldm.plan;
0028:
0029: import java.io.Serializable;
0030: import java.util.Arrays;
0031: import java.util.Enumeration;
0032: import java.util.Vector;
0033:
0034: import org.cougaar.util.Empty;
0035: import org.cougaar.util.SingleElementEnumeration;
0036: import org.cougaar.util.log.Logging;
0037:
0038: /**
0039: * Base class for functions which compute a Score value given an AspectValue.
0040: * Score is a double where LOW_THRESHOLD (0.0) is "good"/"optimal" and
0041: * HIGH_THRESHOLD (1.0) is "bad" (as bad as it gets). ScoringFunction domains
0042: * should be infinite and Range should be 0.0 to 1.0 inclusive.
0043: *
0044: * @note Instances are immutable.
0045: **/
0046: public abstract class ScoringFunction implements Serializable,
0047: Cloneable {
0048:
0049: /** Minimum valid value **/
0050: public static final double LOW_THRESHOLD = 0.0;
0051: /** Maximum valid value **/
0052: public static final double HIGH_THRESHOLD = 1.0;
0053: /** "Best" Score **/
0054: public final static double BEST = LOW_THRESHOLD;
0055: /** "Worst" Score **/
0056: public final static double WORST = HIGH_THRESHOLD;
0057:
0058: /** Typical "Satisfactory" value **/
0059: public final static double OK = 0.5;
0060:
0061: /** A Value to be used when the Score is undefined, equivalent to Double.NaN.
0062: * @note Should be compared via Double.isNaN() rather than with ==
0063: **/
0064: public final static double NOVALUE = Double.NaN;
0065:
0066: protected int aspectType;
0067:
0068: protected ScoringFunction(int type) {
0069: this .aspectType = type;
0070: }
0071:
0072: public abstract Object clone();
0073:
0074: /** Find the/a "Best" value of the function.
0075: * Can be used as a starting point for an allocator.
0076: * If not implemented, returns null.
0077: * @return AspectScorePoint May be null if uncomputable.
0078: */
0079: public abstract AspectScorePoint getBest();
0080:
0081: /** Find the/a "Best" value within a range.
0082: * Specify AspectValue boundaries within the function and get the
0083: * minimum value within those boundaries.
0084: * If not implemented, returns null.
0085: * @param lowerbound
0086: * @param upperbound
0087: * @return AspectScorePoint May be null if uncomputable.
0088: */
0089: public abstract AspectScorePoint getMinInRange(
0090: AspectValue lowerbound, AspectValue upperbound);
0091:
0092: /** Find the/a "Worst" value within a range.
0093: * Specify AspectValue boundaries within the function and get the
0094: * maximum value within those boundaries.
0095: * If not implemented, returns null.
0096: * @param lowerbound
0097: * @param upperbound
0098: * @return AspectScorePoint May be null if uncomputable.
0099: */
0100: public abstract AspectScorePoint getMaxInRange(
0101: AspectValue lowerbound, AspectValue upperbound);
0102:
0103: /** Find all non-1.0 (worst) value ranges within boundaries.
0104: * Specify AspectValue boundaries within the function and get the
0105: * the valid ranges of values within those boundaries.
0106: * If not implemented, returns null.
0107: * @param lowerbound
0108: * @param upperbound
0109: * @return Enumeration{AspectScoreRange} may be null if uncomputable.
0110: */
0111: public abstract Enumeration getValidRanges(AspectValue lowerbound,
0112: AspectValue upperbound);
0113:
0114: /** @return the range over which the scoring function is defined.
0115: * Note that "undefined" == "undifferentiated WORST" score.
0116: * Will always return a non-null value, but one or both values of
0117: * the range may an AspectScorePoing infinity, indicating unbounded range.
0118: * There may be any amount of score variation, including WORST points
0119: * within this range.
0120: **/
0121: public AspectScoreRange getDefinedRange() {
0122: return new AspectScoreRange(AspectScorePoint
0123: .getNEGATIVE_INFINITY(aspectType), AspectScorePoint
0124: .getPOSITIVE_INFINITY(aspectType));
0125: }
0126:
0127: /** Find the Score at a point. 1.0 is worst, 0.0 is best.
0128: * @param value
0129: * @return double The score given an AspectValue.
0130: * @see org.cougaar.planning.ldm.plan.AspectValue
0131: */
0132: public abstract double getScore(AspectValue value);
0133:
0134: // methods that create basic Scoring Functions
0135:
0136: /** Create a ScoringFunction from a set of AspectScorePoints
0137: * @param points A set of AspectScorePoints which define the curve of the function.
0138: */
0139: public static final ScoringFunction createPiecewiseLinearScoringFunction(
0140: Enumeration points) {
0141: return new PiecewiseLinearScoringFunction(points);
0142: }
0143:
0144: /** Create a ScoringFunction from a set of AspectScorePoints. The parameter will not be
0145: * copied, so the points must never be modified.
0146: * @param points A set of AspectScorePoints which define the curve of the function.
0147: */
0148: public static final ScoringFunction createPiecewiseLinearScoringFunction(
0149: AspectScorePoint[] points) {
0150: return new PiecewiseLinearScoringFunction(points);
0151: }
0152:
0153: /** A single point with straight sides in score space
0154: * @param value The single point.
0155: * @return StrictValueScoringFunction
0156: */
0157: public static final ScoringFunction createStrictlyAtValue(
0158: AspectValue value) {
0159: return new StrictValueScoringFunction(value);
0160: }
0161:
0162: /** A single point with slanted sides in score space
0163: * @param value The single point.
0164: * @return PreferredValueScoringFunction
0165: */
0166: public static final ScoringFunction createPreferredAtValue(
0167: AspectValue value, double slope) {
0168: return new PreferredValueScoringFunction(value, slope);
0169: }
0170:
0171: /** A flat basin with straight sides.
0172: * BEST defaults to low point.
0173: * @param low The low point.
0174: * @param high The high point.
0175: * @return StrictBetweenScoringFunction
0176: */
0177: public static final ScoringFunction createStrictlyBetweenValues(
0178: AspectValue low, AspectValue high) {
0179: return new StrictBetweenScoringFunction(low, high);
0180: }
0181:
0182: /** A flat basin with straight sides.
0183: * Just like StrictBetweenScoringFunction, except
0184: * that BEST point is specified, even though the
0185: * Scores of low, best and high are all actually the same.
0186: * @param low The low point.
0187: * @param best The preferred point.
0188: * @param high The high point.
0189: * @return StrictBetweenWithBest
0190: */
0191: public static final ScoringFunction createStrictlyBetweenWithBestValues(
0192: AspectValue low, AspectValue best, AspectValue high) {
0193: return new StrictBetweenWithBest(low, best, high);
0194: }
0195:
0196: /** A typical V-shaped scoring function.
0197: * low and high values are OK, best is BEST,
0198: * score is linear betwen low and best, best and high.
0199: * anything outside the range is WORST.
0200: * The best point need not be centered between low and high.
0201: * @param low The low point.
0202: * @param best The best point.
0203: * @param high The high point.
0204: * @return VScoringFunction
0205: */
0206: public static final ScoringFunction createVScoringFunction(
0207: AspectValue low, AspectValue best, AspectValue high) {
0208: return new VScoringFunction(low, best, high);
0209: }
0210:
0211: /** like createVScoringFunction(low,best,high) except allows specification
0212: * of value of OK value.
0213: **/
0214: public static final ScoringFunction createVScoringFunction(
0215: AspectValue low, AspectValue best, AspectValue high,
0216: double ok) {
0217: return new VScoringFunction(low, best, high, ok);
0218: }
0219:
0220: /** A flat basin with slanted sides
0221: * @param low The low point.
0222: * @param high The high point.
0223: * @return PreferredBetweenScoringFunction
0224: */
0225: public static final ScoringFunction createPreferredBetweenValues(
0226: AspectValue low, AspectValue high, double slope) {
0227: return new PreferredBetweenScoringFunction(low, high, slope);
0228: }
0229:
0230: /** Prefer as close as possible to value from above
0231: * The score at the inflection point is BEST
0232: * @param value The point.
0233: * @return AboveScoringFunction
0234: */
0235: public static final ScoringFunction createNearOrAbove(
0236: AspectValue value, double slope) {
0237: return new AboveScoringFunction(value, slope);
0238: }
0239:
0240: /** Prefer as close as possible to value from below
0241: * The score at the inflection point is BEST;
0242: * @param value The point.
0243: * @return BelowScoringFunction
0244: */
0245: public static final ScoringFunction createNearOrBelow(
0246: AspectValue value, double slope) {
0247: return new BelowScoringFunction(value, slope);
0248: }
0249:
0250: /** Select specific enumerated points where score is allowed :
0251: * disallowed (above threshold) at all other points.
0252: * Note : The implementation ignores range, as enumerations have
0253: * no sense of comparison or continuity.
0254: * @param points array of AspectScorePoints of allowable points
0255: */
0256: public static final ScoringFunction createEnumerated(
0257: AspectScorePoint[] points) {
0258: return new EnumeratedScoringFunction(points);
0259: }
0260:
0261: /** Step function
0262: * The score exactly at the inflection point is BEST
0263: * @param changepoint
0264: * @param prescore
0265: * @param postscore
0266: */
0267: public static final ScoringFunction createStepScoringFunction(
0268: AspectValue changepoint, double prescore, double postscore) {
0269: return new StepScoringFunction(changepoint, prescore, postscore);
0270: }
0271:
0272: /** Constant function
0273: * always has same score
0274: * @param score - the score for all values
0275: */
0276: public static final ScoringFunction createConstantScoringFunction(
0277: double score, int type) {
0278: return new ConstantScoringFunction(score, type);
0279: }
0280:
0281: /** Constant function
0282: * always has same score
0283: * @param score - the score for all values
0284: */
0285: public static final ScoringFunction createConstantScoringFunction(
0286: AspectScorePoint score) {
0287: return new ConstantScoringFunction(score.getScore(), score
0288: .getAspectType());
0289: }
0290:
0291: //
0292: // helper functions for below
0293: //
0294:
0295: protected final static AspectScorePoint newASP(double value,
0296: double score, int type) {
0297: return new AspectScorePoint(AspectValue.newAspectValue(type,
0298: value), score);
0299: }
0300:
0301: /** interpolate a Y given an X and a line segment. px must be in
0302: * the range.
0303: **/
0304: final static double _interpY(double px, double x1, double y1,
0305: double x2, double y2) {
0306: if (x1 == x2)
0307: return y1; // eliminate div0
0308: return y1 + ((px - x1) * ((y2 - y1) / (x2 - x1)));
0309: }
0310:
0311: /** return an AspectScorePoint for the minimum y of the segment
0312: * ((x1,y1) (x2, y2)) in the range of minx-maxx.
0313: * @return WORST if out of range.
0314: **/
0315: final AspectScorePoint _minY(double minx, double maxx, double x1,
0316: double y1, double x2, double y2) {
0317: if (x1 > maxx || x2 < minx)
0318: return newASP(minx, WORST, aspectType);
0319:
0320: if (x1 < minx) {
0321: y1 = _interpY(minx, x1, y1, x2, y2);
0322: x1 = minx;
0323: }
0324: if (x2 > maxx) {
0325: y2 = _interpY(maxx, x1, y1, x2, y2);
0326: x2 = maxx;
0327: }
0328:
0329: // return the lowest y
0330: if (y2 < y1) {
0331: y1 = y2;
0332: x1 = x2;
0333: }
0334:
0335: return newASP(x1, y1, aspectType);
0336: }
0337:
0338: /** return an AspectScorePoint for the maximum y of the segment
0339: * ((x1,y1) (x2, y2)) in the range of minx-maxx.
0340: * @return BEST if out of range.
0341: **/
0342: final AspectScorePoint _maxY(double minx, double maxx, double x1,
0343: double y1, double x2, double y2) {
0344: if (x1 > maxx || x2 < minx)
0345: return newASP(minx, BEST, aspectType);
0346:
0347: if (x1 < minx) {
0348: y1 = _interpY(minx, x1, y1, x2, y2);
0349: x1 = minx;
0350: }
0351: if (x2 > maxx) {
0352: y2 = _interpY(maxx, x1, y1, x2, y2);
0353: x2 = maxx;
0354: }
0355:
0356: // return the highest y
0357: // return the lowest y
0358: if (y2 > y1) {
0359: y1 = y2;
0360: x1 = x2;
0361: }
0362:
0363: return newASP(x1, y1, aspectType);
0364: }
0365:
0366: /** Check a set of AspectScorePoints for validity as the "curve" of
0367: * PiecewiseLinearScoringFunction. Tests used are: must have at least 2 points,
0368: * all points must have the AspectType, values must be strictly increasing, scores
0369: * may not be negative.
0370: * @throws IllegalArgumentException on illegal curve.
0371: **/
0372: public final static void checkValidCurve(AspectScorePoint[] curve) {
0373: try {
0374: int l = curve.length;
0375: if (l == 0) {
0376: throw new IllegalArgumentException("Empty point set");
0377: }
0378: int at = curve[0].getAspectType();
0379: double lv = curve[0].getValue();
0380: double ls = curve[0].getScore();
0381:
0382: for (int i = 1; i < l; i++) {
0383: int t = curve[i].getAspectType();
0384: if (t != at) {
0385: throw new IllegalArgumentException("curve[" + i
0386: + "].getAspectType() is inconsistent (" + t
0387: + " != " + at + ")");
0388: }
0389:
0390: double v = curve[i].getValue();
0391: double s = curve[i].getScore();
0392: if (Double.isNaN(v)) {
0393: throw new IllegalArgumentException("curve[" + i
0394: + "].getValue() is not a number");
0395: }
0396: if (v < lv) {
0397: throw new IllegalArgumentException("curve[" + i
0398: + "].getValue() decreases (" + v + "<" + lv
0399: + ")");
0400: }
0401: if (v == lv && s == ls) {
0402: throw new IllegalArgumentException("curve[" + i
0403: + "] == curve[" + (i - 1) + "]");
0404: }
0405: lv = v;
0406: ls = s;
0407:
0408: if (Double.isNaN(s)) {
0409: throw new IllegalArgumentException("curve[" + i
0410: + "].getScore() is not a number");
0411: }
0412: if (s < LOW_THRESHOLD || s > HIGH_THRESHOLD) {
0413: throw new IllegalArgumentException("curve[" + i
0414: + "].getScore() out of range (" + s + ")");
0415: }
0416: }
0417: } catch (IllegalArgumentException e) {
0418: Logging.getLogger(ScoringFunction.class).error(
0419: "Bad ScoringFunction curve", e);
0420: }
0421: }
0422:
0423: //
0424: // Implementations of the convenience ScoringFunctions above
0425: //
0426:
0427: public static class PiecewiseLinearScoringFunction extends
0428: ScoringFunction {
0429: protected AspectScorePoint curve[];
0430:
0431: public PiecewiseLinearScoringFunction(Enumeration points) {
0432: super (0);
0433: if (points == null || !points.hasMoreElements()) {
0434: throw new IllegalArgumentException(
0435: "Enumeration of points must be non-empty");
0436: }
0437: Vector v = new Vector();
0438: while (points.hasMoreElements()) {
0439: v.addElement(points.nextElement());
0440: }
0441: curve = (AspectScorePoint[]) v
0442: .toArray(new AspectScorePoint[v.size()]);
0443: checkValidCurve(curve);
0444: aspectType = curve[0].getAspectType();
0445: }
0446:
0447: public PiecewiseLinearScoringFunction(AspectScorePoint[] points) {
0448: super (0);
0449: curve = points;
0450: checkValidCurve(curve);
0451: aspectType = curve[0].getAspectType();
0452: }
0453:
0454: public boolean equals(Object o) {
0455: if (o instanceof PiecewiseLinearScoringFunction) {
0456: PiecewiseLinearScoringFunction that = (PiecewiseLinearScoringFunction) o;
0457: return Arrays.equals(this .curve, that.curve);
0458: }
0459: return false;
0460: }
0461:
0462: public Object clone() {
0463: return new PiecewiseLinearScoringFunction(
0464: new Enumeration() {
0465: private int ix = 0;
0466:
0467: public boolean hasMoreElements() {
0468: return ix < curve.length;
0469: }
0470:
0471: public Object nextElement() {
0472: return curve[ix++].clone();
0473: }
0474: });
0475: }
0476:
0477: public double getScore(AspectValue value) {
0478: return getScore(value.getValue());
0479: }
0480:
0481: protected double getScore(double vp) {
0482: int l = curve.length;
0483: AspectScorePoint c1 = null;
0484: for (int i = 0; i < l; i++) {
0485: c1 = curve[i];
0486: double v1 = c1.getValue();
0487: double s1 = c1.getScore();
0488:
0489: if (vp < v1) {
0490: if (i > 0) {
0491: AspectScorePoint c0 = curve[i - 1];
0492: double v0 = c0.getValue();
0493:
0494: if (v1 <= v0)
0495: return s1; // second point wins on undefined.
0496:
0497: double s0 = c0.getScore();
0498: double slope = (s1 - s0) / (v1 - v0);
0499:
0500: return s0 + (vp - v0) * slope;
0501:
0502: } else {
0503: return s1;
0504: }
0505: } // else continue
0506: }
0507: // drop through - flat after last point.
0508: return c1.getScore();
0509: }
0510:
0511: public AspectScorePoint getMinInRange(AspectValue lowerbound,
0512: AspectValue upperbound) {
0513: AspectScorePoint c0 = curve[0];
0514: AspectScorePoint c1;
0515: int l = curve.length;
0516: if (l == 1)
0517: return c0;
0518: double x0 = c0.getValue();
0519: double y0 = c0.getScore();
0520:
0521: double minx = lowerbound.getValue();
0522: double maxx = upperbound.getValue();
0523:
0524: AspectScorePoint bp = null;
0525:
0526: for (int i = 1; i < l; i++) {
0527: c1 = curve[i];
0528: double x1 = c1.getValue();
0529: double y1 = c1.getScore();
0530: AspectScorePoint pp = super ._minY(minx, maxx, x0, y0,
0531: x1, y1);
0532: if (bp == null || pp.getScore() < bp.getScore())
0533: bp = pp;
0534: }
0535: if (bp == null || bp.getScore() == WORST)
0536: return null;
0537: return bp;
0538: }
0539:
0540: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
0541: AspectValue upperbound) {
0542: AspectScorePoint c0 = curve[0];
0543: AspectScorePoint c1;
0544: int l = curve.length;
0545: if (l == 1)
0546: return c0;
0547: double x0 = c0.getValue();
0548: double y0 = c0.getScore();
0549:
0550: double minx = lowerbound.getValue();
0551: double maxx = upperbound.getValue();
0552:
0553: AspectScorePoint bp = null;
0554:
0555: for (int i = 1; i < l; i++) {
0556: c1 = curve[i];
0557: double x1 = c1.getValue();
0558: double y1 = c1.getScore();
0559: AspectScorePoint pp = super ._maxY(minx, maxx, x0, y0,
0560: x1, y1);
0561: if (bp == null || pp.getScore() > bp.getScore())
0562: bp = pp;
0563: }
0564: if (bp == null || bp.getScore() == BEST)
0565: return null;
0566: return bp;
0567: }
0568:
0569: protected class DPair {
0570: public double x0, x1;
0571:
0572: public DPair(double x0, double x1) {
0573: this .x0 = x0;
0574: this .x1 = x1;
0575: }
0576: }
0577:
0578: /** return a vector of DPair instances, each one is a
0579: * range of valid x values. Some may be points (x = x);
0580: * Will return an empty vector if no points are lower than then threshold.
0581: **/
0582: protected Vector getAllValidValues(double thresh) {
0583: AspectScorePoint c0 = curve[0];
0584: double x0 = c0.getValue();
0585: double y0 = c0.getScore();
0586: boolean v0 = (y0 < thresh);
0587: AspectScorePoint c1;
0588: double x1, y1;
0589: boolean v1;
0590:
0591: Vector v = new Vector();
0592:
0593: int l = curve.length;
0594:
0595: if (l == 1) {
0596: if (v0) {
0597: v.addElement(new DPair(x0, x0));
0598: }
0599: return v;
0600: }
0601:
0602: DPair r = null;
0603: for (int i = 1; i < l; i++) {
0604: c1 = curve[i];
0605: x1 = c1.getValue();
0606: y1 = c1.getScore();
0607: v1 = (y1 < thresh);
0608:
0609: // is there a crossing?
0610: if (v0) {
0611: if (v1) { // both valid
0612: if (r == null) {
0613: r = new DPair(x0, x1);
0614: } else {
0615: r.x1 = x1;
0616: }
0617: } else { // v0 valid, v1 not
0618: // find the cross point
0619: double dx = x1 - x0;
0620: if (dx == 0) { // discontinuity
0621: if (r == null) {
0622: v.addElement(new DPair(x0, x0));
0623: } else {
0624: v.addElement(r);
0625: r = null;
0626: }
0627: } else {
0628: double x = x0
0629: + (((x1 - x0) * (thresh - y0)) / (y1 - y0));
0630: if (r == null) {
0631: v.addElement(new DPair(x0, x));
0632: } else {
0633: r.x1 = x;
0634: v.addElement(r);
0635: r = null;
0636: }
0637: }
0638: }
0639: } else {
0640: if (v1) { // v0 not, v1 valid
0641: // find the cross point
0642: double dx = x1 - x0;
0643: if (dx == 0) { // discontinuity
0644: if (r == null) {
0645: r = new DPair(x1, x1);
0646: } else {
0647: // shouldn't actually happen, I think
0648: v.addElement(r);
0649: r = new DPair(x1, x1);
0650: }
0651: } else {
0652: double x = x0
0653: + (((x1 - x0) * (thresh - y0)) / (y1 - y0));
0654: if (r == null) {
0655: r = new DPair(x, x1);
0656: } else {
0657: // again, shouldn't happen
0658: r.x1 = x;
0659: v.addElement(r);
0660: r = null;
0661: }
0662: }
0663: } else { // both invalid
0664: // just advance
0665: }
0666: }
0667:
0668: c0 = c1;
0669: x0 = x1;
0670: y0 = x1;
0671: v0 = v1;
0672: }
0673:
0674: if (r != null)
0675: v.addElement(r);
0676: return v;
0677: }
0678:
0679: public Enumeration getValidRanges(AspectValue lowerbound,
0680: AspectValue upperbound) {
0681: double minx = lowerbound.getValue();
0682: double maxx = upperbound.getValue();
0683:
0684: Vector all = getAllValidValues(WORST);
0685:
0686: Vector clipped = new Vector();
0687:
0688: int sz = all.size();
0689: for (int i = 0; i < sz; i++) {
0690: DPair dp = (DPair) all.elementAt(i);
0691: double x0 = dp.x0;
0692: double y0 = getScore(x0); // nasty
0693: double x1 = dp.x1;
0694: double y1 = getScore(x1); // evil
0695: if (x0 >= maxx)
0696: break; // seg is past range: stop
0697:
0698: if (x1 <= minx) // seg is before range: skip ahead
0699: continue;
0700:
0701: // cut by range start?
0702: if (x0 < minx) {
0703: x0 = minx;
0704: x0 = getScore(x0); // horribly inefficient
0705: }
0706: // cut by range end?
0707: if (x1 > maxx) {
0708: x1 = maxx;
0709: y1 = getScore(x1); // noxious
0710: }
0711:
0712: // add a valid range to clipped vector
0713: clipped.addElement(new AspectScoreRange(newASP(x0, y0,
0714: aspectType), newASP(x1, y1, aspectType)));
0715: }
0716:
0717: return clipped.elements();
0718: }
0719:
0720: public AspectScoreRange getDefinedRange() {
0721: return new AspectScoreRange(curve[0],
0722: curve[curve.length - 1]);
0723: }
0724:
0725: public AspectScorePoint getBest() {
0726: AspectScorePoint best = curve[0];
0727: double bs = best.getScore();
0728: int l = curve.length;
0729: for (int i = 1; i < l; i++) {
0730: AspectScorePoint asp = curve[i];
0731: double s = asp.getScore();
0732: if (s < bs) {
0733: best = asp;
0734: bs = s;
0735: }
0736: }
0737: return best;
0738: }
0739:
0740: public String toString() {
0741: return "<PiecewiseLinear " + curve.length + ">";
0742: }
0743:
0744: }
0745:
0746: /** utility base class for storing single-point SFs **/
0747:
0748: public static abstract class SinglePointScoringFunction extends
0749: ScoringFunction {
0750: protected AspectValue point;
0751:
0752: protected SinglePointScoringFunction(AspectValue value) {
0753: super (value.getAspectType());
0754: point = value;
0755: }
0756:
0757: public AspectValue getPoint() {
0758: return point;
0759: }
0760:
0761: public boolean equals(Object o) {
0762: if (o instanceof SinglePointScoringFunction) {
0763: SinglePointScoringFunction that = (SinglePointScoringFunction) o;
0764: return that.point.equals(this .point);
0765: }
0766: return false;
0767: }
0768: }
0769:
0770: /* A single point with straight sides in score space
0771: * @param value The single point.
0772: * @return StrictValueScoringFunction
0773: */
0774:
0775: public static class StrictValueScoringFunction extends
0776: SinglePointScoringFunction {
0777: private transient AspectScorePoint basp = null;
0778:
0779: private synchronized AspectScorePoint getB() {
0780: if (basp == null) {
0781: basp = new AspectScorePoint(point, BEST);
0782: }
0783: return basp;
0784: }
0785:
0786: public StrictValueScoringFunction(AspectValue value) {
0787: super (value);
0788: }
0789:
0790: public boolean equals(Object o) {
0791: if (o instanceof StrictValueScoringFunction) {
0792: return super .equals(o);
0793: }
0794: return false;
0795: }
0796:
0797: public Object clone() {
0798: return new StrictValueScoringFunction(point);
0799: }
0800:
0801: public AspectScorePoint getBest() {
0802: return getB();
0803: }
0804:
0805: public AspectScorePoint getMinInRange(AspectValue lowerbound,
0806: AspectValue upperbound) {
0807: if (point.isBetween(lowerbound, upperbound)) {
0808: return getB();
0809: } else {
0810: return new AspectScorePoint(lowerbound, WORST);
0811: }
0812: }
0813:
0814: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
0815: AspectValue upperbound) {
0816: if (point.equals(lowerbound) && point.equals(upperbound)) {
0817: return getB();
0818: } else {
0819: return new AspectScorePoint(lowerbound, WORST);
0820: }
0821: }
0822:
0823: public Enumeration getValidRanges(AspectValue lowerbound,
0824: AspectValue upperbound) {
0825: if (point.isBetween(lowerbound, upperbound)) {
0826: AspectScorePoint asp = getB();
0827: return new SingleElementEnumeration(
0828: new AspectScoreRange(asp, asp));
0829: } else {
0830: return Empty.enumeration;
0831: }
0832: }
0833:
0834: public double getScore(AspectValue value) {
0835: if (point.equals(value)) {
0836: return BEST;
0837: } else {
0838: return WORST;
0839: }
0840: }
0841:
0842: public String toString() {
0843: return "<StrictValue " + point + ">";
0844: }
0845: }
0846:
0847: /** classic symmetric V curve.
0848: * Score = min(1.0, | value - point | * slope)
0849: **/
0850:
0851: public static class PreferredValueScoringFunction extends
0852: SinglePointScoringFunction {
0853: protected double slope;
0854:
0855: private transient AspectScorePoint basp = null;
0856:
0857: private synchronized AspectScorePoint getB() {
0858: if (basp == null) {
0859: basp = new AspectScorePoint(point, BEST);
0860: }
0861: return basp;
0862: }
0863:
0864: public boolean equals(Object o) {
0865: if (o instanceof PreferredValueScoringFunction) {
0866: PreferredValueScoringFunction that = (PreferredValueScoringFunction) o;
0867: if (that.slope != this .slope)
0868: return false;
0869: return super .equals(o);
0870: }
0871: return false;
0872: }
0873:
0874: public PreferredValueScoringFunction(AspectValue value,
0875: double slope) {
0876: super (value);
0877: this .slope = slope;
0878: }
0879:
0880: public Object clone() {
0881: return new PreferredValueScoringFunction(point, slope);
0882: }
0883:
0884: public AspectScorePoint getBest() {
0885: return getB();
0886: }
0887:
0888: public Enumeration getValidRanges(AspectValue lowerbound,
0889: AspectValue upperbound) {
0890: double v = point.getValue();
0891: int t = point.getAspectType();
0892: double delta = (slope == 0.0) ? (0.0) : (1 / slope);
0893: AspectValue v0 = AspectValue.newAspectValue(t, v - delta);
0894: AspectValue v1 = AspectValue.newAspectValue(t, v + delta);
0895: AspectScorePoint p0 = new AspectScorePoint(v0, WORST);
0896: AspectScorePoint p1 = new AspectScorePoint(v1, WORST);
0897: return new SingleElementEnumeration(new AspectScoreRange(
0898: p0, p1));
0899: }
0900:
0901: public double getScore(AspectValue value) {
0902: return Math
0903: .min(WORST, slope * Math.abs(point.minus(value)));
0904: }
0905:
0906: public AspectScorePoint getMinInRange(AspectValue lowerbound,
0907: AspectValue upperbound) {
0908: AspectScorePoint asp = null;
0909: if (point.isBetween(lowerbound, upperbound))
0910: asp = getB();
0911: else if (upperbound.isLessThan(point))
0912: asp = new AspectScorePoint(upperbound,
0913: getScore(upperbound));
0914: else
0915: asp = new AspectScorePoint(lowerbound,
0916: getScore(lowerbound));
0917: return asp;
0918: }
0919:
0920: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
0921: AspectValue upperbound) {
0922: //AspectScorePoint asp = null;
0923: double ubScore = getScore(upperbound);
0924: double lbScore = getScore(lowerbound);
0925:
0926: if (lbScore > ubScore)
0927: return new AspectScorePoint(lowerbound, lbScore);
0928: else
0929: return new AspectScorePoint(upperbound, ubScore);
0930: }
0931:
0932: public String toString() {
0933: return "<PreferredValue " + point + " " + slope + ">";
0934: }
0935: }
0936:
0937: public static abstract class TwoPointScoringFunction extends
0938: ScoringFunction {
0939: protected AspectValue point1, point2;
0940:
0941: protected TwoPointScoringFunction(AspectValue point1,
0942: AspectValue point2) {
0943: super (point1.getAspectType());
0944: this .point1 = point1;
0945: this .point2 = point2;
0946: }
0947:
0948: public boolean equals(Object o) {
0949: if (o instanceof TwoPointScoringFunction) {
0950: TwoPointScoringFunction that = (TwoPointScoringFunction) o;
0951: return ((this .point1 == null ? that.point1 == null
0952: : this .point1.equals(that.point1)) && (this .point2 == null ? that.point2 == null
0953: : this .point2.equals(that.point2)));
0954: }
0955: return false;
0956: }
0957:
0958: public AspectValue getPoint1() {
0959: return point1;
0960: }
0961:
0962: public AspectValue getPoint2() {
0963: return point2;
0964: }
0965: }
0966:
0967: public static class VScoringFunction extends
0968: TwoPointScoringFunction {
0969: protected AspectValue best;
0970: protected double ok;
0971:
0972: public VScoringFunction(AspectValue low, AspectValue best,
0973: AspectValue high, double ok) {
0974: super (low, high);
0975: this .best = best;
0976: this .ok = ok;
0977: }
0978:
0979: public VScoringFunction(AspectValue low, AspectValue best,
0980: AspectValue high) {
0981: super (low, high);
0982: this .best = best;
0983: this .ok = OK;
0984: }
0985:
0986: public boolean equals(Object o) {
0987: if (o instanceof VScoringFunction) {
0988: VScoringFunction that = (VScoringFunction) o;
0989: if (that.ok != this .ok)
0990: return false;
0991: if (!that.best.equals(this .best))
0992: return false;
0993: return super .equals(o);
0994: }
0995: return false;
0996: }
0997:
0998: public Object clone() { // clone is probably useless, since it is immutable
0999: return new VScoringFunction(point1, best, point2, ok);
1000: }
1001:
1002: // take the specified best as best
1003: public AspectScorePoint getBest() {
1004: return new AspectScorePoint(best, BEST);
1005: }
1006:
1007: public Enumeration getValidRanges(AspectValue lowerbound,
1008: AspectValue upperbound) {
1009: AspectScorePoint p0 = new AspectScorePoint(point1, ok);
1010: AspectScorePoint p1 = new AspectScorePoint(point2, ok);
1011: return new SingleElementEnumeration(new AspectScoreRange(
1012: p0, p1));
1013: }
1014:
1015: // Although called VScoringFunction, the V hangs from an inverted pedestal
1016: //----+ +----
1017: // | |
1018: // | |
1019: // \ /
1020: // \ /
1021: // \/
1022:
1023: public double getScore(AspectValue value) {
1024: if (value.isBetween(point1, point2)) {
1025: double vp = value.getValue();
1026: double b = best.getValue();
1027: double p = ((vp < b) ? point1 : point2).getValue();
1028: // Value must be BEST at b and ok at p and linear between
1029: return ok + (vp - p) * (BEST - ok) / (b - p);
1030: } else {
1031: return WORST;
1032: }
1033: }
1034:
1035: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1036: AspectValue upperbound) {
1037: double ubScore = getScore(upperbound);
1038: double lbScore = getScore(lowerbound);
1039:
1040: if (ubScore < lbScore)
1041: return new AspectScorePoint(upperbound, ubScore);
1042: return new AspectScorePoint(lowerbound, lbScore);
1043: }
1044:
1045: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1046: AspectValue upperbound) {
1047:
1048: // best is in range
1049: if (best.isBetween(lowerbound, upperbound))
1050: return getBest();
1051:
1052: // Out of range completely
1053: if (point1.isGreaterThan(upperbound))
1054: return new AspectScorePoint(upperbound, WORST);
1055:
1056: // Out of range completely
1057: if (point2.isLessThan(lowerbound))
1058: return new AspectScorePoint(lowerbound, WORST);
1059:
1060: // Ends on low
1061: if (point1.equals(upperbound))
1062: return new AspectScorePoint(upperbound, ok);
1063:
1064: // Starts on high
1065: if (point2.equals(lowerbound))
1066: return new AspectScorePoint(lowerbound, ok);
1067:
1068: // On the downslope
1069: if (upperbound.isLessThan(best))
1070: return new AspectScorePoint(upperbound,
1071: getScore(upperbound));
1072:
1073: // On the upslope
1074: return new AspectScorePoint(lowerbound,
1075: getScore(lowerbound));
1076: }
1077:
1078: public String toString() {
1079: return "<V " + point1 + "-" + point2 + " best=" + best
1080: + " ok=" + ok + ">";
1081: }
1082: }
1083:
1084: public static class StrictBetweenScoringFunction extends
1085: TwoPointScoringFunction {
1086: public StrictBetweenScoringFunction(AspectValue low,
1087: AspectValue high) {
1088: super (low, high);
1089: }
1090:
1091: public boolean equals(Object o) {
1092: if (o instanceof StrictBetweenScoringFunction) {
1093: return super .equals(o);
1094: }
1095: return false;
1096: }
1097:
1098: public Object clone() {
1099: return new StrictBetweenScoringFunction(point1, point2);
1100: }
1101:
1102: //arbitrarily take the low value as best for now
1103: public AspectScorePoint getBest() {
1104: return new AspectScorePoint(point1, BEST);
1105: }
1106:
1107: public Enumeration getValidRanges(AspectValue lowerbound,
1108: AspectValue upperbound) {
1109: AspectScorePoint p0 = new AspectScorePoint(point1, WORST);
1110: AspectScorePoint p1 = new AspectScorePoint(point2, WORST);
1111: return new SingleElementEnumeration(new AspectScoreRange(
1112: p0, p1));
1113: }
1114:
1115: public double getScore(AspectValue value) {
1116: if (value.isBetween(point1, point2))
1117: return BEST;
1118: else
1119: return WORST;
1120: }
1121:
1122: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1123: AspectValue upperbound) {
1124:
1125: // Begins out of range
1126: if (!lowerbound.isBetween(point1, point2))
1127: return new AspectScorePoint(lowerbound, WORST);
1128:
1129: // Ends out of range
1130: if (!upperbound.isBetween(point1, point2))
1131: return new AspectScorePoint(upperbound, WORST);
1132:
1133: // Arbitrarily send back lowerbound
1134: return new AspectScorePoint(lowerbound, BEST);
1135: }
1136:
1137: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1138: AspectValue upperbound) {
1139:
1140: // starts between
1141: if (lowerbound.isBetween(point1, point2))
1142: return new AspectScorePoint(lowerbound, BEST);
1143:
1144: // ends between
1145: if (upperbound.isBetween(point1, point2))
1146: return new AspectScorePoint(upperbound, BEST);
1147:
1148: // surrounds
1149: if (point1.isBetween(lowerbound, upperbound)
1150: && point2.isBetween(lowerbound, upperbound))
1151: // return a point halfway between point1 and point2
1152: return new AspectScorePoint(AspectValue.newAspectValue(
1153: lowerbound.getAspectType(),
1154: (point1.getValue() + point2.getValue()) / 2),
1155: BEST);
1156:
1157: // Not in range at all, arbitrarily send back upperbound
1158: return new AspectScorePoint(upperbound, WORST);
1159: }
1160:
1161: public String toString() {
1162: return "<StrictBetween " + point1 + "-" + point2 + ">";
1163: }
1164: }
1165:
1166: public static class StrictBetweenWithBest extends
1167: StrictBetweenScoringFunction {
1168:
1169: protected AspectValue best;
1170:
1171: public StrictBetweenWithBest(AspectValue low, AspectValue best,
1172: AspectValue high) {
1173: super (low, high);
1174: this .best = best;
1175: }
1176:
1177: public boolean equals(Object o) {
1178: if (o instanceof StrictBetweenWithBest) {
1179: StrictBetweenWithBest that = (StrictBetweenWithBest) o;
1180: if (this .best.equals(that.best)) {
1181: return super .equals(o);
1182: }
1183: }
1184: return false;
1185: }
1186:
1187: public Object clone() {
1188: return new StrictBetweenWithBest(point1, best, point2);
1189: }
1190:
1191: // take the specified best as best
1192: public AspectScorePoint getBest() {
1193: return new AspectScorePoint(best, BEST);
1194: }
1195:
1196: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1197: AspectValue upperbound) {
1198: if (best.isBetween(lowerbound, upperbound))
1199: return getBest();
1200:
1201: return super .getMinInRange(lowerbound, upperbound);
1202: }
1203:
1204: public String toString() {
1205: return "<StrictBetween " + point1 + "-" + point2 + " best="
1206: + best + ">";
1207: }
1208: }
1209:
1210: public static class PreferredBetweenScoringFunction extends
1211: TwoPointScoringFunction {
1212: protected double slope;
1213:
1214: public PreferredBetweenScoringFunction(AspectValue low,
1215: AspectValue high, double slope) {
1216: super (low, high);
1217: this .slope = slope;
1218: }
1219:
1220: public boolean equals(Object o) {
1221: if (o instanceof PreferredBetweenScoringFunction) {
1222: PreferredBetweenScoringFunction that = (PreferredBetweenScoringFunction) o;
1223: if (that.slope != this .slope)
1224: return false;
1225: return super .equals(o);
1226: }
1227: return false;
1228: }
1229:
1230: public Object clone() {
1231: return new PreferredBetweenScoringFunction(point1, point2,
1232: slope);
1233: }
1234:
1235: //arbitrarily take the low value as best for now
1236: public AspectScorePoint getBest() {
1237: return new AspectScorePoint(point1, BEST);
1238: }
1239:
1240: public Enumeration getValidRanges(AspectValue lowerbound,
1241: AspectValue upperbound) {
1242: int t = point1.getAspectType();
1243: double delta = (slope == 0.0) ? (0.0) : (1 / slope);
1244: AspectValue v0 = AspectValue.newAspectValue(t, point1
1245: .getValue()
1246: - delta);
1247: AspectValue v1 = AspectValue.newAspectValue(t, point1
1248: .getValue()
1249: + delta);
1250: AspectScorePoint p0 = new AspectScorePoint(v0, WORST);
1251: AspectScorePoint p1 = new AspectScorePoint(v1, WORST);
1252: return new SingleElementEnumeration(new AspectScoreRange(
1253: p0, p1));
1254: }
1255:
1256: public double getScore(AspectValue value) {
1257: if (value.isLessThan(point1)) {
1258: return Math.min(WORST, slope * point1.minus(value));
1259: } else if (value.isGreaterThan(point2)) {
1260: return Math.min(WORST, slope * value.minus(point2));
1261: } else {
1262: return BEST;
1263: }
1264: }
1265:
1266: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1267: AspectValue upperbound) {
1268: // range spans basin
1269: if (point1.isBetween(lowerbound, upperbound)
1270: && point2.isBetween(lowerbound, upperbound)) {
1271: // pick halfway point in basin
1272: AspectValue halfpoint = AspectValue.newAspectValue(
1273: lowerbound.getAspectType(),
1274: (point1.getValue() + point2.getValue()) / 2);
1275: return new AspectScorePoint(halfpoint,
1276: getScore(halfpoint));
1277: }
1278:
1279: double ubScore = getScore(upperbound);
1280: double lbScore = getScore(lowerbound);
1281:
1282: // range begins or ends in basin
1283: if (lowerbound.isBetween(point1, point2))
1284: return new AspectScorePoint(lowerbound, lbScore);
1285: if (upperbound.isBetween(point1, point2))
1286: return new AspectScorePoint(upperbound, ubScore);
1287:
1288: // one of the end points is the min
1289: if (lbScore < ubScore)
1290: return new AspectScorePoint(lowerbound, lbScore);
1291: return new AspectScorePoint(upperbound, ubScore);
1292:
1293: }
1294:
1295: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1296: AspectValue upperbound) {
1297: double ubScore = getScore(upperbound);
1298: double lbScore = getScore(lowerbound);
1299: // max has to be one of the end points
1300: if (lbScore > ubScore)
1301: return new AspectScorePoint(lowerbound, lbScore);
1302: return new AspectScorePoint(upperbound, ubScore);
1303:
1304: }
1305:
1306: public String toString() {
1307: return "<PreferredBetween " + point1 + "-" + point2
1308: + " slope=" + slope + ">";
1309: }
1310: }
1311:
1312: public static class AboveScoringFunction extends
1313: SinglePointScoringFunction {
1314: private double slope;
1315:
1316: public AboveScoringFunction(AspectValue value, double slope) {
1317: super (value);
1318: this .slope = slope;
1319: }
1320:
1321: public boolean equals(Object o) {
1322: if (o instanceof AboveScoringFunction) {
1323: AboveScoringFunction that = (AboveScoringFunction) o;
1324: if (that.slope != this .slope)
1325: return false;
1326: return super .equals(o);
1327: }
1328: return false;
1329: }
1330:
1331: public Object clone() {
1332: return new AboveScoringFunction(point, slope);
1333: }
1334:
1335: public AspectScorePoint getBest() {
1336: return new AspectScorePoint(point, BEST);
1337: }
1338:
1339: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1340: AspectValue upperbound) {
1341: AspectScorePoint asp = null;
1342: if (point.isBetween(lowerbound, upperbound))
1343: asp = new AspectScorePoint(point, BEST);
1344: else if (point.isGreaterThan(upperbound))
1345: asp = new AspectScorePoint(upperbound, WORST);
1346: else
1347: asp = new AspectScorePoint(lowerbound,
1348: getScore(lowerbound));
1349: return asp;
1350: }
1351:
1352: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1353: AspectValue upperbound) {
1354: AspectScorePoint asp = null;
1355: double ubScore = getScore(upperbound);
1356: double lbScore = getScore(lowerbound);
1357: // One of the endpoints is Max
1358: if (lbScore > ubScore)
1359: asp = new AspectScorePoint(lowerbound, lbScore);
1360: else
1361: asp = new AspectScorePoint(upperbound, ubScore);
1362: return asp;
1363: }
1364:
1365: public double getScore(AspectValue value) {
1366: if (!(value.isLessThan(point))) {
1367: return Math.min(WORST, slope * value.minus(point));
1368: } else {
1369: return WORST;
1370: }
1371: }
1372:
1373: public Enumeration getValidRanges(AspectValue lowerbound,
1374: AspectValue upperbound) {
1375:
1376: double lbScore = getScore(lowerbound);
1377: double ubScore = getScore(upperbound);
1378:
1379: // Whole range in safe area
1380: if ((lbScore < WORST) && (ubScore < WORST)) {
1381: // return range
1382: AspectScorePoint p0 = new AspectScorePoint(lowerbound,
1383: lbScore);
1384: AspectScorePoint p1 = new AspectScorePoint(upperbound,
1385: ubScore);
1386: return new SingleElementEnumeration(
1387: new AspectScoreRange(p0, p1));
1388: }
1389:
1390: // Whole range in WORST area
1391: if (!point.isBetween(lowerbound, upperbound)
1392: && (lbScore == WORST) && (ubScore == WORST)) {
1393: // return empty set
1394: return Empty.enumeration;
1395: }
1396:
1397: AspectScorePoint p0;
1398: AspectScorePoint p1;
1399: // Starts before point
1400: if (lowerbound.isLessThan(point))
1401: // use point
1402: p0 = new AspectScorePoint(point, getScore(point));
1403: else
1404: // use lb
1405: p0 = new AspectScorePoint(lowerbound, lbScore);
1406:
1407: if (ubScore < WORST)
1408: // use ub
1409: p1 = new AspectScorePoint(upperbound, ubScore);
1410: else {
1411: double delta = (slope == 0.0) ? (0.0) : (1 / slope);
1412: AspectValue v1 = AspectValue.newAspectValue(point
1413: .getAspectType(), point.getValue() + delta);
1414: p1 = new AspectScorePoint(v1, WORST);
1415: }
1416: return new SingleElementEnumeration(new AspectScoreRange(
1417: p0, p1));
1418: }
1419:
1420: public String toString() {
1421: return "<Above " + point + " " + slope + ">";
1422: }
1423: }
1424:
1425: public static class BelowScoringFunction extends
1426: SinglePointScoringFunction {
1427: private double slope;
1428:
1429: public BelowScoringFunction(AspectValue value, double slope) {
1430: super (value);
1431: this .slope = slope;
1432: }
1433:
1434: public boolean equals(Object o) {
1435: if (o instanceof BelowScoringFunction) {
1436: BelowScoringFunction that = (BelowScoringFunction) o;
1437: if (that.slope != this .slope)
1438: return false;
1439: return super .equals(o);
1440: }
1441: return false;
1442: }
1443:
1444: public Object clone() {
1445: return new BelowScoringFunction(point, slope);
1446: }
1447:
1448: public AspectScorePoint getBest() {
1449: return new AspectScorePoint(point, BEST);
1450: }
1451:
1452: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1453: AspectValue upperbound) {
1454: AspectScorePoint asp = null;
1455: if (point.isBetween(lowerbound, upperbound))
1456: asp = new AspectScorePoint(point, BEST);
1457: else if (point.isGreaterThan(upperbound))
1458: asp = new AspectScorePoint(upperbound,
1459: getScore(upperbound));
1460: else
1461: asp = new AspectScorePoint(lowerbound,
1462: getScore(lowerbound));
1463: return asp;
1464: }
1465:
1466: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1467: AspectValue upperbound) {
1468: AspectScorePoint asp = null;
1469: double ubScore = getScore(upperbound);
1470: double lbScore = getScore(lowerbound);
1471: // One of the endpoints is Max
1472: if (lbScore > ubScore)
1473: asp = new AspectScorePoint(lowerbound, lbScore);
1474: else
1475: asp = new AspectScorePoint(upperbound, ubScore);
1476: return asp;
1477: }
1478:
1479: public double getScore(AspectValue value) {
1480: if (!(value.isGreaterThan(point))) {
1481: return Math.min(WORST, slope * point.minus(value));
1482: } else {
1483: return WORST;
1484: }
1485: }
1486:
1487: public Enumeration getValidRanges(AspectValue lowerbound,
1488: AspectValue upperbound) {
1489: double lbScore = getScore(lowerbound);
1490: double ubScore = getScore(upperbound);
1491:
1492: // Whole range in safe area
1493: if ((lbScore < WORST) && (ubScore < WORST)) {
1494: // return range
1495: AspectScorePoint p0 = new AspectScorePoint(lowerbound,
1496: lbScore);
1497: AspectScorePoint p1 = new AspectScorePoint(upperbound,
1498: ubScore);
1499: return new SingleElementEnumeration(
1500: new AspectScoreRange(p0, p1));
1501: }
1502:
1503: // Whole range in WORST area
1504: if (!point.isBetween(lowerbound, upperbound)
1505: && (lbScore == WORST) && (ubScore == WORST)) {
1506: // return empty set
1507: return Empty.enumeration;
1508: }
1509:
1510: AspectScorePoint p0;
1511: if (lbScore < WORST) {
1512: // lowerbound in good range
1513: p0 = new AspectScorePoint(lowerbound, lbScore);
1514: } else {
1515: double delta = (slope == 0.0) ? (0.0) : (1 / slope);
1516: AspectValue v0 = AspectValue.newAspectValue(point
1517: .getAspectType(), point.getValue() - delta);
1518: p0 = new AspectScorePoint(v0, WORST);
1519: }
1520:
1521: AspectScorePoint p1;
1522: if (ubScore < WORST)
1523: // use upperbound
1524: p1 = new AspectScorePoint(upperbound, ubScore);
1525: else
1526: // use point
1527: p1 = new AspectScorePoint(point, getScore(point));
1528:
1529: return new SingleElementEnumeration(new AspectScoreRange(
1530: p0, p1));
1531:
1532: }
1533:
1534: public String toString() {
1535: return "<Below " + point + " " + slope + ">";
1536: }
1537: }
1538:
1539: public static class StepScoringFunction extends
1540: SinglePointScoringFunction {
1541: double v0;
1542: double v1;
1543:
1544: public StepScoringFunction(AspectValue changepoint,
1545: double prescore, double postscore) {
1546: super (changepoint);
1547: v0 = prescore;
1548: v1 = postscore;
1549: }
1550:
1551: public boolean equals(Object o) {
1552: if (o instanceof StepScoringFunction) {
1553: StepScoringFunction that = (StepScoringFunction) o;
1554: if (that.v0 != this .v0)
1555: return false;
1556: if (that.v1 != this .v1)
1557: return false;
1558: return super .equals(o);
1559: }
1560: return false;
1561: }
1562:
1563: public Object clone() {
1564: return new StepScoringFunction(point, v0, v1);
1565: }
1566:
1567: public double getScore(AspectValue value) {
1568: if (value.isLessThan(point))
1569: return v0;
1570: else
1571: return v1;
1572: }
1573:
1574: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1575: AspectValue upperbound) {
1576:
1577: double lbScore = getScore(lowerbound);
1578: double ubScore = getScore(upperbound);
1579:
1580: if (lbScore < ubScore)
1581: return new AspectScorePoint(lowerbound, lbScore);
1582: return new AspectScorePoint(upperbound, ubScore);
1583: }
1584:
1585: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1586: AspectValue upperbound) {
1587: double lbScore = getScore(lowerbound);
1588: double ubScore = getScore(upperbound);
1589: if (lbScore > ubScore)
1590: return new AspectScorePoint(lowerbound, lbScore);
1591: return new AspectScorePoint(upperbound, ubScore);
1592: }
1593:
1594: /**
1595: * return changepoint if its score is lower,
1596: * otherwise return a point -x away from changepoint
1597: **/
1598: public AspectScorePoint getBest() {
1599: // which is lower?
1600: double lowscore = (v0 < v1) ? v0 : v1;
1601:
1602: // _____
1603: // |
1604: // |
1605: // |____
1606: //
1607: if (getScore(point) == lowscore)
1608: return new AspectScorePoint(point, lowscore);
1609:
1610: // v1 < v0, but point is at zero
1611: //______
1612: //
1613: //
1614: if (point.getValue() == 0)
1615: return new AspectScorePoint(point, getScore(point));
1616:
1617: // ______
1618: // |
1619: // |
1620: // _____|
1621: // arbitrary value in the range between 0 and point
1622: // what if point<0 ?
1623: double away = 1.0;
1624: double pointValue = point.getValue();
1625: while (pointValue - away <= 0)
1626: away = away * 0.1;
1627:
1628: return new AspectScorePoint(AspectValue.newAspectValue(
1629: point.getAspectType(), pointValue - away), lowscore);
1630:
1631: }
1632:
1633: public Enumeration getValidRanges(AspectValue lowerbound,
1634: AspectValue upperbound) {
1635:
1636: double lbScore = getScore(lowerbound);
1637: double ubScore = getScore(upperbound);
1638:
1639: // Either the "bad" score is less than 1.0
1640: // -or-
1641: // The range is on the "good" side of the point
1642: if (((v0 < WORST) && (v1 < WORST))
1643: || ((lbScore < WORST) && (ubScore < WORST))) {
1644: // send back the whole range
1645: AspectScorePoint p0 = new AspectScorePoint(lowerbound,
1646: lbScore);
1647: AspectScorePoint p1 = new AspectScorePoint(upperbound,
1648: ubScore);
1649: return new SingleElementEnumeration(
1650: new AspectScoreRange(p0, p1));
1651: }
1652:
1653: if (point.isBetween(lowerbound, upperbound)) {
1654: if (v0 == WORST) {
1655: // return from point to upperbound
1656: AspectScorePoint p0 = new AspectScorePoint(point,
1657: v1);
1658: AspectScorePoint p1 = new AspectScorePoint(
1659: upperbound, v1);
1660: return new SingleElementEnumeration(
1661: new AspectScoreRange(p0, p1));
1662: } else {
1663: // return from lowerbound to point
1664: AspectScorePoint p0 = new AspectScorePoint(
1665: lowerbound, v0);
1666: AspectScorePoint p1 = new AspectScorePoint(point,
1667: v1);
1668: return new SingleElementEnumeration(
1669: new AspectScoreRange(p0, p1));
1670: }
1671: }
1672:
1673: // Entire range WORST
1674: return Empty.enumeration;
1675: }
1676:
1677: public String toString() {
1678: return "<Step " + point + " " + v0 + " " + v1 + ">";
1679: }
1680: }
1681:
1682: /* A scoring function for an enumerated set of AspectScorePoints
1683: * Ranges are ignored, since enumerations have no sense of comparison or
1684: * continuity. All values not in enumeration give a value of WORST.
1685: */
1686: public static class EnumeratedScoringFunction extends
1687: ScoringFunction {
1688:
1689: // Maintain list of all provided points
1690: // NOTE : Could implement this as a hash table if lists get long
1691: private AspectScorePoint[] my_points;
1692:
1693: /**
1694: * Constructor for EnumeratedScoringFunction
1695: **/
1696: public EnumeratedScoringFunction(AspectScorePoint[] points) {
1697: super (points[0].getAspectType());
1698: my_points = points;
1699: }
1700:
1701: public boolean equals(Object o) {
1702: if (o instanceof EnumeratedScoringFunction) {
1703: EnumeratedScoringFunction that = (EnumeratedScoringFunction) o;
1704: return Arrays.equals(this .my_points, that.my_points);
1705: }
1706: return false;
1707: }
1708:
1709: public Object clone() {
1710: AspectScorePoint[] newPoints = new AspectScorePoint[my_points.length];
1711: for (int i = 0; i < newPoints.length; i++) {
1712: newPoints[i] = my_points[i];
1713: }
1714: return new EnumeratedScoringFunction(newPoints);
1715: }
1716:
1717: // Find aspectscore point with max/min based on flag
1718: private AspectScorePoint findExtreme(boolean find_max) {
1719: AspectScorePoint current = null;
1720: for (int i = 0; i < my_points.length; i++) {
1721: if ((current == null)
1722: || ((find_max == false) && (current.getScore() > my_points[i]
1723: .getScore()))
1724: || ((find_max == true) && (current.getScore() < my_points[i]
1725: .getScore()))) {
1726: current = my_points[i];
1727: }
1728: }
1729: return current;
1730: }
1731:
1732: /**
1733: * getBest AspectScorePoint - iterate over all and find lowest score
1734: */
1735: public AspectScorePoint getBest() {
1736: return findExtreme(false);
1737: }
1738:
1739: /**
1740: * Find minimum value - ignore range information
1741: **/
1742: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1743: AspectValue upperbound) {
1744: return findExtreme(true);
1745: }
1746:
1747: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1748: AspectValue upperbound) {
1749: return getBest();
1750: }
1751:
1752: /**
1753: * Enumeration of all valid ranges : in this case, all points
1754: * represent a discrete range in and of themselves
1755: **/
1756: public Enumeration getValidRanges(AspectValue lowerbound,
1757: AspectValue upperbound) {
1758: Vector v = new Vector();
1759: for (int i = 0; i < my_points.length; i++) {
1760: v.addElement(new AspectScoreRange(my_points[i],
1761: my_points[i]));
1762: }
1763: return v.elements();
1764: }
1765:
1766: /**
1767: * Iterate over all points and find if there is a point for this
1768: * value. If not, return WORST.
1769: **/
1770: public double getScore(AspectValue value) {
1771: for (int i = 0; i < my_points.length; i++) {
1772: if (my_points[i].getValue() == value.getValue()) {
1773: return my_points[i].getScore();
1774: }
1775: }
1776: return WORST;
1777: }
1778:
1779: public String toString() {
1780: return "<Enumerated " + my_points.length + ">";
1781: }
1782: }
1783:
1784: public static class ConstantScoringFunction extends ScoringFunction {
1785: double score = BEST;
1786:
1787: public ConstantScoringFunction(double score, int aspectType) {
1788: super (aspectType);
1789: this .score = score;
1790: }
1791:
1792: public ConstantScoringFunction(int aspectType) {
1793: super (aspectType);
1794: }
1795:
1796: public boolean equals(Object o) {
1797: if (o instanceof ConstantScoringFunction) {
1798: ConstantScoringFunction that = (ConstantScoringFunction) o;
1799: if (this .aspectType == that.aspectType) {
1800: return this .score == that.score;
1801: }
1802: }
1803: return false;
1804: }
1805:
1806: public Object clone() {
1807: return new ConstantScoringFunction(score, aspectType);
1808: }
1809:
1810: public AspectScorePoint getBest() {
1811: return newASP(0, score, aspectType);
1812: }
1813:
1814: public AspectScorePoint getMinInRange(AspectValue lowerbound,
1815: AspectValue upperbound) {
1816: return newASP(lowerbound.getValue(), score, aspectType);
1817: }
1818:
1819: public AspectScorePoint getMaxInRange(AspectValue lowerbound,
1820: AspectValue upperbound) {
1821: return newASP(upperbound.getValue(), score, aspectType);
1822: }
1823:
1824: public Enumeration getValidRanges(AspectValue lowerbound,
1825: AspectValue upperbound) {
1826: AspectScorePoint low = new AspectScorePoint(lowerbound,
1827: score);
1828: AspectScorePoint high = new AspectScorePoint(upperbound,
1829: score);
1830: return new SingleElementEnumeration(new AspectScoreRange(
1831: low, high));
1832: }
1833:
1834: public double getScore(AspectValue value) {
1835: return score;
1836: }
1837:
1838: public String toString() {
1839: return "<Constant " + score + ">";
1840: }
1841: }
1842:
1843: // Test fix for bug 2536. Eventually turn this into a regression test
1844: public static void main(String[] args) {
1845: long p1 = Long.parseLong(args[0]);
1846: long b = Long.parseLong(args[1]);
1847: long p2 = Long.parseLong(args[2]);
1848: ScoringFunction sf = createVScoringFunction(AspectValue
1849: .newAspectValue(AspectType.START_TIME, p1), AspectValue
1850: .newAspectValue(AspectType.START_TIME, b), AspectValue
1851: .newAspectValue(AspectType.START_TIME, p2));
1852: for (int i = 3; i < args.length; i++) {
1853: long p = Long.parseLong(args[i]);
1854: AspectValue av = AspectValue.newAspectValue(
1855: AspectType.START_TIME, p);
1856: double score = sf.getScore(av);
1857: System.out.println(p + ": " + score);
1858: }
1859: }
1860: }
1861:
1862: /*
1863: public AspectScorePoint getMinInRange(AspectValue lowerbound, AspectValue upperbound) {
1864: }
1865: public AspectScorePoint getMaxInRange(AspectValue lowerbound, AspectValue upperbound) {
1866: }
1867: public Enumeration getValidRanges(AspectValue lowerbound, AspectValue upperbound) {
1868: }
1869: public AspectScoreRange getDefinedRange() {
1870: }
1871:
1872: */
|