UNPKG

finitedomain

Version:

A fast feature rich finite domain solver

892 lines (832 loc) 20.5 kB
import expect from '../../fixtures/mocha_proxy.fixt'; import Solver from '../../../src/solver'; import { config_clone, } from '../../../src/config'; import { space_getUnsolvedVarCount, _space_getUnsolvedVarNamesFresh, } from '../../../src/space'; import case20160611 from './2016-06-11'; import case20160618 from './2016-06-18'; import case20160618_slim from './2016-06-18.slim'; import case20160618_slim2 from './2016-06-18.slim2'; import case20160803 from './2016-08-03'; import case201608032 from './2016-08-03-2'; import case20161017 from './2016-10-17'; describe('exports/export.cases.spec', function() { this.timeout(60000); // takes long under istanbul / even longer under travis it('should solve 2016-06-11 twice', function() { var solver = new Solver({config: config_clone(case20160611)}); console.log('run 1:'); solver.solve({log: 1, max: 1}); console.log('run 2:'); solver.solve({log: 1, max: 1}); }); it('should proceed past prepare for 2016-06-13', function() { // from path_solver spec; "solver w/ rules: vars ~= var" // regression; breaks while prepare()ing let config = { _class: '$config', varStratConfig: { type: 'naive', }, valueStratName: 'min', targetedVars: 'all', varDistOptions: {}, timeoutCallback: undefined, allVarNames: ['0', '_ROOT_BRANCH_', '2', 'align', '4', 'text_align', 'size', 'cols', '8', '9', '10', '11'], allConstraints: [ {_class: '$constraint', name: 'eq', varIndexes: [1, 0], param: undefined}, {_class: '$constraint', name: 'gte', varIndexes: [3, 0], param: undefined}, {_class: '$constraint', name: 'gte', varIndexes: [5, 0], param: undefined}, {_class: '$constraint', name: 'gte', varIndexes: [6, 0], param: undefined}, {_class: '$constraint', name: 'gte', varIndexes: [7, 0], param: undefined}, {_class: '$constraint', name: 'reifier', varIndexes: [3, 0, 8], param: 'eq'}, {_class: '$constraint', name: 'reifier', varIndexes: [1, 0, 9], param: 'eq'}, {_class: '$constraint', name: 'gte', varIndexes: [8, 9], param: undefined}, {_class: '$constraint', name: 'reifier', varIndexes: [7, 0, 10], param: 'eq'}, {_class: '$constraint', name: 'reifier', varIndexes: [1, 0, 11], param: 'eq'}, {_class: '$constraint', name: 'gte', varIndexes: [10, 11], param: undefined}, {_class: '$constraint', name: 'neq', varIndexes: [5, 6], param: undefined}, {_class: '$constraint', name: 'neq', varIndexes: [6, 6], param: undefined}, ], constantCache: {'1': '0', '2': '2', '3': '4'}, initialDomains: [ [1, 1], [0, 1], [2, 2], [1, 2], [3, 3], [1, 3], [1, 3], [1, 2], [0, 1], [0, 1], [0, 1], [0, 1], ], }; var solver = new Solver({config: config_clone(config)}); solver.solve({log: 1, max: 1}); }); describe('2016-06-18', function() { it('should not take infinite time', function() { var solver = new Solver({config: config_clone(case20160618)}); solver.solve({log: 1, max: 1}); }); }); describe('2016-06-18.slim', function() { // drilled down the test case and fixed a bug related to what was left. // unfortunately it was not the actual bug. it('should not take infinite time', function() { var solver = new Solver({config: config_clone(case20160618_slim)}); solver.solve({log: 1, max: 1}); }); it('trimmed down version', function() { var solver = new Solver(); solver.declRange('x12', 1, 8, { valtype: 'list', list: [4, 3, 2, 1, 5, 6, 7, 8], }); solver.declRange('x19', 1, 8, { valtype: 'list', list: [4, 3, 2, 1, 5, 6, 7, 8], }); solver.declRange('x26', 1, 8, { valtype: 'list', list: [4, 3, 2, 1, 5, 6, 7, 8], }); solver.declRange('x152', 0, 1); solver.declRange('x164', 0, 1); solver.declRange('x165', 0, 1); solver.declRange('x166', 0, 1); solver.declRange('x171', 0, 100000000); solver.declRange('x172', 0, 1); solver.declRange('x207', 0, 100000000); solver.declRange('x208', 0, 1); solver.declRange('x209', 0, 100000000); solver.declRange('x210', 0, 100000000); solver.declRange('x211', 0, 1); solver.declRange('x214', 0, 100000000); solver.declRange('x215', 0, 1); solver.isEq('x12', 4, 'x164'); solver.isEq('x19', 4, 'x165'); solver.isEq('x26', 4, 'x166'); solver.sum(['x164', 'x165', 'x166'], 'x171'); solver.isEq('x171', 3, 'x172'); solver.sum(['x172'], 'x210'); solver.sum(['x152'], 'x209'); solver.isEq('x209', 'x210', 'x211'); solver.isGte('x207', 1, 'x208'); solver.plus('x209', 1, 'x214'); solver.isEq('x210', 'x214', 'x215'); solver.neq('x211', 'x215'); solver.solve({log: 1, max: 1}); }); it('empty array version', function() { // this version took a long time because fd didnt reject immediately // when encountering the empty domain (you'll need some large open // domains for the effect to show) let config = { _class: '$config', nextVarStrat: 'naive', valueStratName: 'min', targetedVars: 'all', varDistOptions: {}, timeoutCallback: undefined, allVarNames: [ '0', '_ROOT_BRANCH_', 'SECTION', '3', '4', '5', '6', 'VERSE_INDEX', '8', 'ITEM_INDEX', '10', '11', 'width', 'color', 'post_type', 'state', 'SECTION&n=1', 'VERSE_INDEX&n=1', 'ITEM_INDEX&n=1', 'width&n=1', 'color&n=1', 'post_type&n=1', 'state&n=1', 'SECTION&n=2', 'VERSE_INDEX&n=2', 'ITEM_INDEX&n=2', 'width&n=2', 'color&n=2', 'post_type&n=2', 'state&n=2', 'SECTION&n=3', 'VERSE_INDEX&n=3', 'ITEM_INDEX&n=3', 'width&n=3', 'color&n=3', 'post_type&n=3', 'state&n=3', 'SECTION&n=4', 'VERSE_INDEX&n=4', 'ITEM_INDEX&n=4', 'width&n=4', 'color&n=4', 'post_type&n=4', 'state&n=4', 'SECTION&n=5', 'VERSE_INDEX&n=5', 'ITEM_INDEX&n=5', 'width&n=5', 'color&n=5', 'post_type&n=5', 'state&n=5', 'SECTION&n=6', 'VERSE_INDEX&n=6', 'ITEM_INDEX&n=6', 'width&n=6', 'color&n=6', 'post_type&n=6', 'state&n=6', '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', '165', '166', '167', '168', '169', '170', '171', '172', '173', '174', '175', '176', '177', '178', '179', '180', '181', '182', '183', '184', '185', '186', '187', '188', '189', '190', '191', '192', '193', '194', '195', '196', '197', '198', '199', '200', '201', '202', '203', '204', '205', '206', '207', '208', '209', '210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '220', '221', '222', '223', '224', '225', '226', '227', '228', '229', '230', '231', '232', '233', '234', '235', '236', '237', '238', '239', '240', '241', '242', '243', '244', '245', '246', '247', '248', '249', '250', '251', '252', '253', '254', '255', '256', '257', '258', '259', '260', '261', '262', '263', '264', '265', '266', '267', '268', '269', '270', '271', '272', '273', '274'], allConstraints: [ {_class: '$constraint', name: 'reifier', varIndexes: [0, 1, 2], param: 'eq'}, ], constantCache: {}, initialDomains: [ [3, 3], // 8 [4, 4], // 4 [0, 1], // 0 [], [], [], [], [], [], [], [], [], [1, 8], // 12 [], [], [], [], [], [], [1, 8], // 21 [], [], [], [], [], [], [1, 8], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [0, 1], // 152 [], [], [], [], [], [], [], [], [], [], [], [0, 1], // 165 [0, 1], [0, 1], [], [], [], [], [0, 3], // 171 [0, 1], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [0, 100000000], // 207 [0, 1], [0, 100000000], // 209 [0, 100000000], // 210 [0, 1], [0, 100000000], // 212 [0, 100000000], [0, 100000000], // 214 [0, 1], // 215 [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], ], }; let solver = new Solver({config: config_clone(config)}); solver.solve({log: 1, max: 1}); }); it('show that a sum for one var is an eq', function() { let solver = new Solver(); solver.decl('A', [0, 1]); solver.decl('B', [0, 100000000]); solver.sum(['A'], 'B'); let solutionSum = solver.solve({log: 1, max: 3}); expect(solutionSum, 'sum').to.eql([{A: 0, B: 0}, {A: 1, B: 1}]); let solver2 = new Solver(); solver2.decl('A', [0, 1]); solver2.decl('B', [0, 100000000]); solver2.eq('A', 'B'); let solutionEq = solver2.solve({log: 1, max: 3}); expect(solutionEq, 'eq').to.eql([{A: 0, B: 0}, {A: 1, B: 1}]); // so... expect(solutionEq, 'eq=sum').to.eql(solutionSum); }); it('show that a sum for two vars is a plus', function() { let solver = new Solver(); solver.decl('A', [0, 1]); solver.decl('B', [0, 1]); solver.decl('C', [0, 1]); solver.sum(['A', 'B'], 'C'); let solutionSum = solver.solve({log: 1, max: 3}); expect(solutionSum, 'sum').to.eql([{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 1}]); let solver2 = new Solver(); solver2.decl('A', [0, 1]); solver2.decl('B', [0, 1]); solver2.decl('C', [0, 1]); solver2.plus('A', 'B', 'C'); let solutionPlus = solver2.solve({log: 1, max: 3}); expect(solutionPlus, 'eq').to.eql([{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 1}]); // so... expect(solutionPlus, 'eq=sum').to.eql(solutionSum); }); describe('unconstrained vars were still requiring a solve, exploding the runtime', function() { it('unconstrained AB should not appear in spaces unsolved vars list', function() { let solver = new Solver(); solver.decl('A', [19, 20]); solver.decl('B', [20, 21]); let solution = solver.solve(); expect(solution).to.eql([{A: [19, 20], B: [20, 21]}]); // return all valid values that still satisfy all constraints (->none) expect(space_getUnsolvedVarCount(solver._space, solver.config)).to.eql(0); // should not target A or B because they are unconstrained }); it('constrained AB should appear in spaces unsolved vars list', function() { let solver = new Solver(); solver.decl('A', [19, 20]); solver.decl('B', [21, 22]); solver.neq('A', 'B'); let solution = solver.solve({vars: ['A', 'B']}); expect(solution).to.eql([{A: 19, B: 21}, {A: 19, B: 22}, {A: 20, B: 21}, {A: 20, B: 22}]); // now it must solve. note: result will be different when we optimize neq to "solve" properly expect(_space_getUnsolvedVarNamesFresh(solver._space, solver.config).sort()).to.eql(['A', 'B']); // should A and B because they are under neq }); }); }); describe('2016-06-18.slim2', function() { it('should not take infinite time', function() { var solver = new Solver({config: config_clone(case20160618_slim2)}); solver.solve({log: 1, max: 1}); }); describe('sum seems to be broken', function() { it('sum seems to be broken', function() { let solver = new Solver(); solver.decl('A', [0, 1]); solver.decl('B', [0, 1]); solver.decl('S', [2, 2]); solver.sum(['A', 'B'], 'S'); let solution = solver.solve({log: 1}); expect(solution).to.eql([{A: 1, B: 1, S: 2}]); }); it('sum seems to be broken', function() { let solver = new Solver(); solver.decl('A', [0, 1]); solver.decl('B', [0, 1]); solver.decl('S', [2, 2]); solver.plus('A', 'B', 'S'); let solution = solver.solve({log: 1}); expect(solution).to.eql([{A: 1, B: 1, S: 2}]); }); }); it('same but as a fresh solver', function() { let solver = new Solver(); solver.decl('x0', 1); solver.decl('x3', 2); solver.decl('x8', 3); solver.decl('x160', [0, 1]); solver.decl('x168', [0, 1]); solver.decl('x169', [0, 1]); solver.decl('x170', [0, 1]); solver.decl('x173', [0, 100000000]); solver.decl('x174', [0, 1]); solver.decl('x179', [0, 100000000]); solver.decl('x180', [0, 1]); solver.decl('x203', [0, 100000000]); solver.decl('x204', [0, 1]); solver.decl('x261', [0, 1]); solver.isEq('x173', 'x8', 'x174'); solver.sum(['x168', 'x169', 'x170'], 'x179'); solver.isEq('x179', 'x8', 'x180'); solver.sum(['x160', 'x180'], 'x203'); solver.isGte('x203', 1, 'x204'); solver.sum(['x261', 'x204'], 'x3'); solver.solve({log: 1, max: 1}); }); it('reified should solve immediately', function() { let solver = new Solver(); solver.decl('x0', 1); solver.decl('x203', [0, 100000000]); solver.decl('x204', [0, 1]); solver.isGte('x203', 1, 'x204'); solver.solve({log: 1, max: 1}); }); }); describe('2016-08-03', function() { it('should have three solutions', function() { var solver = new Solver({config: config_clone(case20160803)}); solver.solve({log: 1, max: 4}); expect(solver.solutions.length).to.eql(3); }); }); describe('2016-08-03-2', function() { it('should solve quickly', function() { var solver = new Solver({config: config_clone(case201608032)}); solver.solve({log: 1, max: 1}); expect(solver.solutions.length).to.eql(1); }); }); describe('2016-10-17', function() { it('should solve at all (regression)', function() { var solver = new Solver({config: config_clone(case20161017)}); solver.solve({log: 1, max: 1}); expect(solver.solutions.length).to.eql(1); }); }); });