mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
146 lines
5.3 KiB
JavaScript
146 lines
5.3 KiB
JavaScript
var getNonZeroWeightedTerms = function (costTerms, costWeights) {
|
|
if (typeof costWeights === 'number') {
|
|
return costWeights ? costTerms : [];
|
|
} else {
|
|
var terms = [];
|
|
for (var i = 0; i < costTerms.length; i++) {
|
|
if (costWeights[i]) {
|
|
terms.push(costTerms[i]);
|
|
}
|
|
}
|
|
return terms;
|
|
}
|
|
};
|
|
|
|
// See comments on minimizeWeightedSum and maximizeWeightedSum.
|
|
var minMaxWS = function (solver, solution, costTerms, costWeights, options,
|
|
isMin) {
|
|
var curSolution = solution;
|
|
var curCost = curSolution.getWeightedSum(costTerms, costWeights);
|
|
|
|
var optFormula = options && options.formula;
|
|
var weightedSum = (optFormula || Logic.weightedSum(costTerms, costWeights));
|
|
|
|
var progress = options && options.progress;
|
|
var strategy = options && options.strategy;
|
|
|
|
// array of terms with non-zero weights, populated on demand
|
|
var nonZeroTerms = null;
|
|
|
|
if (isMin && curCost > 0) {
|
|
// try to skip straight to 0 cost, because if it works, it could
|
|
// save us some time
|
|
if (progress) {
|
|
progress('trying', 0);
|
|
}
|
|
var zeroSolution = null;
|
|
nonZeroTerms = getNonZeroWeightedTerms(costTerms, costWeights);
|
|
var zeroSolution = solver.solveAssuming(Logic.not(Logic.or(nonZeroTerms)));
|
|
if (zeroSolution) {
|
|
curSolution = zeroSolution;
|
|
curCost = 0;
|
|
}
|
|
}
|
|
|
|
if (isMin && strategy === 'bottom-up') {
|
|
for (var trialCost = 1; trialCost < curCost; trialCost++) {
|
|
if (progress) {
|
|
progress('trying', trialCost);
|
|
}
|
|
var costIsTrialCost = Logic.equalBits(
|
|
weightedSum, Logic.constantBits(trialCost));
|
|
var newSolution = solver.solveAssuming(costIsTrialCost);
|
|
if (newSolution) {
|
|
curSolution = newSolution;
|
|
curCost = trialCost;
|
|
break;
|
|
}
|
|
}
|
|
} else if (strategy && strategy !== 'default') {
|
|
throw new Error("Bad strategy: " + strategy);
|
|
} else {
|
|
strategy = 'default';
|
|
}
|
|
|
|
if (strategy === 'default') {
|
|
// for minimization, count down from current cost. for maximization,
|
|
// count up.
|
|
while (isMin ? curCost > 0 : true) {
|
|
if (progress) {
|
|
progress('improving', curCost);
|
|
}
|
|
var improvement = (isMin ? Logic.lessThan : Logic.greaterThan)(
|
|
weightedSum, Logic.constantBits(curCost));
|
|
var newSolution = solver.solveAssuming(improvement);
|
|
if (! newSolution) {
|
|
break;
|
|
}
|
|
solver.require(improvement);
|
|
curSolution = newSolution;
|
|
curCost = curSolution.getWeightedSum(costTerms, costWeights);
|
|
}
|
|
}
|
|
|
|
if (isMin && curCost === 0) {
|
|
// express the requirement that the weighted sum be 0 in an efficient
|
|
// way for the solver (all terms with non-zero weights must be 0)
|
|
if (! nonZeroTerms) {
|
|
nonZeroTerms = getNonZeroWeightedTerms(costTerms, costWeights);
|
|
}
|
|
solver.forbid(nonZeroTerms);
|
|
} else {
|
|
solver.require(Logic.equalBits(weightedSum, Logic.constantBits(curCost)));
|
|
}
|
|
|
|
if (progress) {
|
|
progress('finished', curCost);
|
|
}
|
|
|
|
return curSolution;
|
|
};
|
|
|
|
// Minimize (or maximize) the dot product of costTerms and
|
|
// costWeights, and further, require (as in solver.require) that the
|
|
// value of the dot product be equal to the optimum found. Returns a
|
|
// valid solution where this optimum is achieved.
|
|
//
|
|
// `solution` must be a current valid solution as returned from
|
|
// `solve` or `solveAssuming`. It is used as a starting point (to
|
|
// evaluate the current cost).
|
|
//
|
|
// costWeights is an array (of same length as costTerms) or a single
|
|
// WholeNumber.
|
|
//
|
|
// if the caller passes options.formula, it should be the formula
|
|
// Logic.weightedSum(costTerms, costWeights). The optimizer will use
|
|
// this existing formula rather than generating a new one (for
|
|
// efficiency). The optimizer still wants to know the terms and
|
|
// weights, because it is more efficient for it to evaluate the
|
|
// current cost using them directly rather than the formula.
|
|
//
|
|
// options.progress: a function that takes two arguments, to call at
|
|
// particular times during optimization. Called with arguments
|
|
// ('improving', cost) when about to search for a way to improve on
|
|
// `cost`, and called with arguments ('finished', cost) when the
|
|
// optimum is reached. There's also ('trying', cost) when a cost
|
|
// is tried directly (which is usually done with 0 right off the bat).
|
|
//
|
|
// options.strategy: a string hinting how to go about the optimization.
|
|
// the default strategy (option absent or 'default') is to work down
|
|
// from the current cost for minimization or up from the current cost
|
|
// for maximization, and iteratively insist that the cost be made lower
|
|
// if possible. For minimization, the alternate strategy 'bottom-up' is
|
|
// available, which starts at 0 and tries ever higher costs until one
|
|
// works. All strategies first try and see if a cost of 0 is possible.
|
|
|
|
// ("costTerms" is kind of a misnomer since they may be Formulas or Terms.)
|
|
Logic.Solver.prototype.minimizeWeightedSum = function (solution, costTerms,
|
|
costWeights, options) {
|
|
return minMaxWS(this, solution, costTerms, costWeights, options, true);
|
|
};
|
|
|
|
Logic.Solver.prototype.maximizeWeightedSum = function (solution, costTerms,
|
|
costWeights, options) {
|
|
return minMaxWS(this, solution, costTerms, costWeights, options, false);
|
|
};
|