Implement proper cost function

Lots of things happened here:
* Refactored how cost minimization is expressed
* Cost function now distinguishes major/minor/patch/rest
* Order of terms is improved
* Reachability analysis of catalog makes benchmarks much faster
* Lock down versions of special packages before solving rest (perf)
* Explaining conflicts no longer crashes on cycles
* Antigravity for patches (and wrap nums, prereleases, etc.) so that
  we take bug fixes to indirect dependencies.

TODO:
* Refuse to make breaking changes to root reps
* Make sure we don't have antigravity changing any previous solutions
  for no reason
* Proper pre-release handling
* Unit tests
This commit is contained in:
David Greenspan
2015-02-06 20:47:31 -08:00
parent b95135bf05
commit a5df9f84ab
5 changed files with 781 additions and 624 deletions

View File

@@ -210,7 +210,7 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
"coffee-rails": "4.0.1",
"coffee-script": "2.2.0",
"coffee-script-source": "1.7.0",
"d3-rails": "3.1.4",
"d3-rails": "3.1.10",
"default-value-for": "3.0.0",
"devise": "3.0.4",
"devise-async": "0.8.0",
@@ -219,7 +219,7 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
"faraday": "0.9.0",
"github-markup": "1.1.0",
"haml": "4.0.5",
"haml-rails": "0.5.1",
"haml-rails": "0.5.3",
"hashie": "2.0.3",
"hike": "1.2.3",
"httpauth": "0.2.1",
@@ -236,7 +236,7 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
"oauth": "0.4.7",
"oauth2": "0.8.1",
"omniauth": "1.1.4",
"omniauth-github": "1.0.2",
"omniauth-github": "1.0.3",
"omniauth-google-oauth2": "0.2.2",
"omniauth-oauth": "1.0.1",
"omniauth-oauth2": "1.1.1",
@@ -244,10 +244,10 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
"orm-adapter": "0.5.0",
"polyglot": "0.3.4",
"posix-spawn": "0.3.8",
"protected-attributes": "1.0.3",
"protected-attributes": "1.0.7",
"rack": "1.5.2",
"rack-test": "0.6.2",
"rails": "4.0.0",
"rails": "4.0.4",
"rails-observers": "0.1.2",
"railties": "4.0.0",
"rake": "10.1.1",
@@ -264,7 +264,7 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
"thread-safe": "0.3.1",
"tilt": "1.4.1",
"treetop": "1.4.15",
"turbolinks": "2.2.0",
"turbolinks": "2.2.1",
"tzinfo": "0.3.39",
"uglifier": "2.5.0",
"warden": "1.2.3"

View File

@@ -107,7 +107,7 @@ Tinytest.add("constraint solver - simple exact + regular deps", function (test)
"sparky-forms": "1.1.2",
"forms": "1.0.1",
"sparkle": "2.1.1",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2"
});
@@ -115,7 +115,7 @@ Tinytest.add("constraint solver - simple exact + regular deps", function (test)
"sparky-forms": "1.1.2",
"forms": "1.0.1",
"sparkle": "2.1.1",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2",
"awesome-dropdown": "1.5.0",
"dropdown": "1.2.2"
@@ -133,7 +133,7 @@ Tinytest.add("constraint solver - non-exact direct dependency", function (test)
"sparky-forms": "1.1.2",
"forms": "1.0.1",
"sparkle": "2.1.1",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2",
"awesome-dropdown": "1.5.0",
"dropdown": "1.2.2"
@@ -249,7 +249,7 @@ Tinytest.add("constraint solver - previousSolution", function (test) {
"sparky-forms": "1.0.0",
"awesome-dropdown": "1.4.0",
"dropdown": "1.2.2",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2",
"sparkle": "2.1.1"
});
@@ -260,7 +260,7 @@ Tinytest.add("constraint solver - previousSolution", function (test) {
"sparky-forms": "1.1.2",
"forms": "1.0.1",
"sparkle": "2.1.1",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2"
});
@@ -270,7 +270,7 @@ Tinytest.add("constraint solver - previousSolution", function (test) {
"sparky-forms": "1.0.0",
"awesome-dropdown": "1.4.0",
"dropdown": "1.2.2",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2",
"sparkle": "2.1.1"
}, { previousSolution: {
@@ -284,7 +284,7 @@ Tinytest.add("constraint solver - previousSolution", function (test) {
"sparky-forms": "1.1.2",
"forms": "1.0.1",
"sparkle": "2.1.1",
"jquery-widgets": "1.0.0",
"jquery-widgets": "1.0.2",
"jquery": "1.8.2"
}, { previousSolution: {
"sparky-forms": "1.0.0"

View File

@@ -3717,147 +3717,74 @@ Tinytest.add("constraint solver - input - slow solve", function (test) {
test.equal(
formatSolution(CS.PackagesResolver._resolveWithInput(input)),
formatSolution({
// Old solver:
// "answer":{
// "autopublish":"1.0.2",
// "u2622:persistent-session":"0.2.1",
// "blaze":"2.0.4",
// "random":"1.0.2",
// "mobile-status-bar":"1.0.2",
// "deps":"1.0.6",
// "follower-livedata":"1.0.3",
// "spacebars":"1.0.4",
// "spacebars-compiler":"1.0.4",
// "launch-screen":"1.0.1",
// "iron:location":"1.0.6",
// "http":"1.0.9",
// "json":"1.0.2",
// "check":"1.0.3",
// "retry":"1.0.2",
// "id-map":"1.0.2",
// "reactive-dict":"1.0.5",
// "mrt:moment":"2.8.1",
// "callback-hook":"1.0.2",
// "meteor":"1.1.4",
// "fastclick":"1.0.2",
// "minifiers":"1.1.3",
// "mrt:jquery-ui-sortable":"1.10.3",
// "webapp":"1.1.5",
// "ejson":"1.0.5",
// "skinnygeek1010:parse-form":"0.2.1",
// "iron:controller":"1.0.6",
// "base64":"1.0.2",
// "url":"1.0.3",
// "blaze-tools":"1.0.2",
// "ddp":"1.0.13",
// "iron:core":"1.0.6",
// "splendido:accounts-templates-semantic-ui":"0.0.4",
// "observe-sequence":"1.0.4",
// "reactive-var":"1.0.4",
// "webapp-hashing":"1.0.2",
// "mongo":"1.0.11",
// "htmljs":"1.0.3",
// "ui":"1.0.5",
// "amplify":"1.0.0",
// "meteor-platform":"1.2.1",
// "ordered-dict":"1.0.2",
// "session":"1.0.5",
// "livedata":"1.0.12",
// "templating":"1.0.10",
// "binary-heap":"1.0.2",
// "mizzao:timesync":"0.2.2",
// "tracker":"1.0.4",
// "autoupdate":"1.1.4",
// "html-tools":"1.0.3",
// "reload":"1.1.2",
// "less":"1.0.12",
// "application-configuration":"1.0.4",
// "gfk:notifications":"1.1.1",
// "underscore":"1.0.2",
// "iron:dynamic-template":"1.0.6",
// "routepolicy":"1.0.3",
// "iron:router":"1.0.6",
// "insecure":"1.0.2",
// "iron:layout":"1.0.6",
// "geojson-utils":"1.0.2",
// "minimongo":"1.0.6",
// "iron:url":"1.0.6",
// "jquery":"1.11.2",
// "boilerplate-generator":"1.0.2",
// "iron:middleware-stack":"1.0.6",
// "logging":"1.0.6"
// },
"answer": {
"accounts-base": "1.1.3",
"amplify": "1.0.0",
"application-configuration": "1.0.4",
"autopublish": "1.0.2",
"autoupdate": "1.1.4",
"base64": "1.0.2",
"binary-heap": "1.0.2",
"blaze": "2.0.4",
"blaze-tools": "1.0.2",
"boilerplate-generator": "1.0.2",
"callback-hook": "1.0.2",
"check": "1.0.3",
"coffeescript": "1.0.5",
"ddp": "1.0.13",
"deps": "1.0.6",
"ejson": "1.0.5",
"fastclick": "1.0.2",
"follower-livedata": "1.0.3",
"geojson-utils": "1.0.2",
"gfk:notifications": "1.1.1",
"html-tools": "1.0.3",
"htmljs": "1.0.3",
"http": "1.0.9",
"id-map": "1.0.2",
"insecure": "1.0.2",
"iron:core": "0.3.4",
"iron:dynamic-template": "0.4.1",
"iron:layout": "0.4.1",
"iron:router": "0.9.4",
"jquery": "1.11.2",
"json": "1.0.2",
"launch-screen": "1.0.1",
"less": "1.0.12",
"livedata": "1.0.12",
"localstorage": "1.0.2",
"logging": "1.0.6",
"meteor": "1.1.4",
"meteor-platform": "1.2.1",
"minifiers": "1.1.3",
"minimongo": "1.0.6",
"mizzao:timesync": "0.2.2",
"mobile-status-bar": "1.0.2",
"mongo": "1.0.11",
"mrt:jquery-ui-sortable": "1.10.3",
"mrt:moment": "2.8.1",
"observe-sequence": "1.0.4",
"ordered-dict": "1.0.2",
"random": "1.0.2",
"reactive-dict": "1.0.5",
"reactive-var": "1.0.4",
"reload": "1.1.2",
"retry": "1.0.2",
"routepolicy": "1.0.3",
"service-configuration": "1.0.3",
"session": "1.0.5",
"sha": "1.0.2",
"skinnygeek1010:parse-form": "0.2.1",
"softwarerero:accounts-t9n": "1.0.0",
"spacebars": "1.0.4",
"spacebars-compiler": "1.0.4",
"splendido:accounts-templates-core": "0.11.0",
"splendido:accounts-templates-semantic-ui": "0.11.0",
"templating": "1.0.10",
"tracker": "1.0.4",
"u2622:persistent-session": "0.2.1",
"ui": "1.0.5",
"underscore": "1.0.2",
"url": "1.0.3",
"webapp": "1.1.5",
"webapp-hashing": "1.0.2"
"answer":{
"autopublish":"1.0.2",
"u2622:persistent-session":"0.2.1",
"blaze":"2.0.4",
"random":"1.0.2",
"mobile-status-bar":"1.0.2",
"deps":"1.0.6",
"follower-livedata":"1.0.3",
"spacebars":"1.0.4",
"spacebars-compiler":"1.0.4",
"launch-screen":"1.0.1",
"iron:location":"1.0.6",
"http":"1.0.9",
"json":"1.0.2",
"check":"1.0.3",
"retry":"1.0.2",
"id-map":"1.0.2",
"reactive-dict":"1.0.5",
"mrt:moment":"2.8.1",
"callback-hook":"1.0.2",
"meteor":"1.1.4",
"fastclick":"1.0.2",
"minifiers":"1.1.3",
"mrt:jquery-ui-sortable":"1.10.3",
"webapp":"1.1.5",
"ejson":"1.0.5",
"skinnygeek1010:parse-form":"0.2.1",
"iron:controller":"1.0.6",
"base64":"1.0.2",
"url":"1.0.3",
"blaze-tools":"1.0.2",
"ddp":"1.0.13",
"iron:core":"1.0.6",
"splendido:accounts-templates-semantic-ui":"0.0.4",
"observe-sequence":"1.0.4",
"reactive-var":"1.0.4",
"webapp-hashing":"1.0.2",
"mongo":"1.0.11",
"htmljs":"1.0.3",
"ui":"1.0.5",
"amplify":"1.0.0",
"meteor-platform":"1.2.1",
"ordered-dict":"1.0.2",
"session":"1.0.5",
"livedata":"1.0.12",
"templating":"1.0.10",
"binary-heap":"1.0.2",
"mizzao:timesync":"0.2.2",
"tracker":"1.0.4",
"autoupdate":"1.1.4",
"html-tools":"1.0.3",
"reload":"1.1.2",
"less":"1.0.12",
"application-configuration":"1.0.4",
"gfk:notifications":"1.1.1",
"underscore":"1.0.2",
"iron:dynamic-template":"1.0.6",
"routepolicy":"1.0.3",
"iron:router":"1.0.6",
"insecure":"1.0.2",
"iron:layout":"1.0.6",
"geojson-utils":"1.0.2",
"minimongo":"1.0.6",
"iron:url":"1.0.6",
"jquery":"1.11.2",
"boilerplate-generator":"1.0.2",
"iron:middleware-stack":"1.0.6",
"logging":"1.0.6"
},
"neededToUseUnanticipatedPrereleases":false
}));

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,16 @@
var minMax = function (solver, solution, costTerms, costWeights, optFormula, isMin) {
var minMax = 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;
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);
@@ -19,6 +25,10 @@ var minMax = function (solver, solution, costTerms, costWeights, optFormula, isM
solver.require((isMin ? Logic.lessThanOrEqual : Logic.greaterThanOrEqual)(
weightedSum, Logic.constantBits(curCost)));
if (progress) {
progress('finished', curCost);
}
return curSolution;
};
@@ -34,10 +44,10 @@ var minMax = function (solver, solution, costTerms, costWeights, optFormula, isM
// more efficient for it to evaluate the current cost using them directly
// rather than the formula.
Logic.Solver.prototype.minimize = function (solution, costTerms, costWeights, optFormula) {
return minMax(this, solution, costTerms, costWeights, optFormula, true);
Logic.Solver.prototype.minimize = function (solution, costTerms, costWeights, options) {
return minMax(this, solution, costTerms, costWeights, options, true);
};
Logic.Solver.prototype.maximize = function (solution, costTerms, costWeights, optFormula) {
return minMax(this, solution, costTerms, costWeights, optFormula, false);
Logic.Solver.prototype.maximize = function (solution, costTerms, costWeights, options) {
return minMax(this, solution, costTerms, costWeights, options, false);
};