All files / universal/gpii/node_modules/canopyMatchMaker/src CanopyMatchMakerUtilities.js

100% Statements 80/80
100% Branches 30/30
100% Functions 13/13
100% Lines 77/77
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164                                    3x 3x   3x   3x 676x 676x 7078x 7078x   676x 676x     3x 816x 816x 1959x 1959x 256x   1703x 1703x 560x                 3x 167x 167x 684x   167x             3x 165x 674x   165x 165x 674x               3x 163x 163x     163x   163x   163x   163x 163x 670x   163x     3x     165x 1527x 1527x     165x 674x 674x 12x     662x 662x 6989x 6989x 1460x 1460x     662x 237x 237x 237x       425x 425x     165x             3x 6915x 6915x 16041x 16041x 16041x 5305x     1610x     3x   3x 14017x   14017x 7160x   6857x         3x 10764x    
/*!
GPII Canopy Matchmaker
 
Copyright 2012 OCAD University
Copyright 2012 Antranig Basman
 
Licensed under the New BSD license. You may not use this file except in
compliance with this License.
 
The research leading to these results has received funding from the European Union's
Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016.
 
You may obtain a copy of the License at
https://github.com/GPII/universal/blob/master/LICENSE.txt
*/
 
"use strict";
 
var fluid = fluid || require("infusion");
var gpii = gpii || fluid.registerNamespace("gpii");
 
fluid.registerNamespace("gpii.canopyMatchMaker.utils");
 
gpii.canopyMatchMaker.utils.computeFitness = function (leaves, solution, ontologicalMetricFunction) {
    var vector = [];
    for (var i = 0; i < leaves.length; ++i) {
        var leaf = leaves[i];
        vector[i] = ontologicalMetricFunction(leaf, solution);
    }
    vector = vector.sort(gpii.canopyMatchMaker.utils.sortDescending);
    return vector;
};
 
gpii.canopyMatchMaker.utils.compareFitness = function (solA, solB) {
    var i = 0, fitA = solA.fitness, fitB = solB.fitness;
    for (; ; ++i) {
        var endA = i === fitA.length, endB = i === fitB.length;
        if (endA || endB) {
            return endA && endB ? 0 : (endB ? -1 : 1);
        }
        var diff = fitB[i] - fitA[i];
        if (diff !== 0) {
            return diff;
        }
    }
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.sortSolutions = function (solutions) {
    var solArr = [];
    for (var i in solutions) {
        solArr.push(solutions[i]);
    }
    return solArr.sort(gpii.canopyMatchMaker.utils.compareFitness);
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.rankSolutions = function (leaves, solrecs, ontologicalMetricFunction) {
    fluid.each(solrecs, function (solrec) {
        solrec.fitness = gpii.canopyMatchMaker.utils.computeFitness(leaves, solrec.skeleton, ontologicalMetricFunction);
    });
    var solArr = gpii.canopyMatchMaker.utils.sortSolutions(solrecs);
    return fluid.transform(solArr, function (value) {
        return value.index;
    });
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.disposeStrategy = function (leaves, solrecs, data, ontologyMetadata) {
    var ontologicalMetricName = fluid.get(ontologyMetadata, "ontologicalMetricFunction") || "gpii.canopyMatchMaker.utils.prefixLength";
    var ontologicalMetricFunction = fluid.getGlobalValue(ontologicalMetricName);
    // if we already have priority for some solutions, accept these and reject any applications
    // of same type.
    gpii.matchMakerFramework.utils.disposeFromPriority(solrecs, data);
    // rank remaining solutions based on fit to preferences
    var ranked = gpii.canopyMatchMaker.utils.rankSolutions(leaves, solrecs, ontologicalMetricFunction);
    // dispose based on canopy
    var disposed = gpii.canopyMatchMaker.utils.disposeFromCanopy(leaves, ranked, solrecs, data, ontologicalMetricFunction);
    // re-key by solution id:
    var togo = {};
    fluid.each(disposed, function (val) {
        togo[val.index] = val;
    });
    return togo;
};
 
gpii.canopyMatchMaker.utils.disposeFromCanopy = function (leaves, ranked, solrecs, data, ontologicalMetricFunction) {
    // Set default canopy to negative leaf depth. To raise the canopy, a preference needs to
    // match at least one level into the leaf.
    var canopy = fluid.transform(leaves, function (leaf) {
        var leafDepth = fluid.pathUtil.parseEL(leaf).length;
        return -leafDepth;
    });
 
    for (var i = 0; i < ranked.length; ++i) {
        var sol = solrecs[ranked[i]];
        if (sol.disposition === "reject") {
            continue;
        }
 
        var inCanopy = false;
        for (var j = 0; j < leaves.length; ++j) {
            var depth = ontologicalMetricFunction(leaves[j], sol.skeleton);
            if (depth > canopy[j]) {
                inCanopy = true;
                canopy[j] = depth;
            }
        }
        if (inCanopy) {
            sol.disposition = "accept";
            sol.dispositionReason = "Was the solution of this type that best fit user preferences";
            gpii.matchMakerFramework.utils.rejectFromTypes(data.solutionTypes[sol.index],
                data.solutionTypeMapping, solrecs, "Another solution (" + sol.index +
                ") covering more preferences was found found.");
        } else {
            sol.disposition = "reject";
            sol.dispositionReason = "Was not in canopy.";
        }
    }
    return solrecs;
};
 
/** Returns a non-positive number indicating by how many path segments the supplied
 * path fails to index correctly into the supplied model. A return value of 0
 * indicates that the path indexes fully
 */
gpii.canopyMatchMaker.utils.prefixLength = function (path, model) {
    var segs = fluid.pathUtil.parseEL(path);
    for (var i = 0; i < segs.length; ++i) {
        var seg = segs[i];
        model = model[seg];
        if (model === undefined) {
            return i - segs.length;
        }
    }
    return 0;
};
 
fluid.registerNamespace("gpii.canopyMatchMaker.utils.ISO24751");
 
gpii.canopyMatchMaker.utils.ISO24751.ontologicalMetric = function (path, model) {
    var segs = fluid.pathUtil.parseEL(path);
    // if it's an application setting and the application is not matching, consider worst fit
    if (segs[0] === "applications" && fluid.get(model, ["applications", segs[1]]) === undefined) {
        return -Infinity;
    } else { // else do fitting as usual
        return gpii.canopyMatchMaker.utils.prefixLength(path, model);
    }
};
 
// only used by canopyMatchMaker - moved from MatchMakerFramework
gpii.canopyMatchMaker.utils.sortDescending = function (numA, numB) {
    return numB - numA;
};