UNPKG

finitedomain

Version:

A fast feature rich finite domain solver

275 lines (240 loc) 7.69 kB
import expect from '../fixtures/mocha_proxy.fixt'; import { stripAnonVarsFromArrays, } from '../fixtures/domain.fixt'; import { countSolutions, } from '../fixtures/lib'; import Solver from '../../src/solver'; import { space_getVarSolveState, } from '../../src/space'; describe('src/solver.list.spec', function() { it('should exist', function() { expect(Solver).to.be.a('function'); }); describe('explicit list per var of distribution', function() { it('should try values in order of the list', function() { let solver = new Solver({}); solver.declRange('V1', 1, 4, { valtype: 'list', list: [2, 4, 3, 1], }); solver.declRange('V2', 1, 4, { valtype: 'list', list: [3, 1, 4, 2], }); solver.gt('V1', 0); solver.gt('V2', 0); let solutions = solver.solve(); expect(countSolutions(solver)).to.equal(16); expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 2, V2: 3}, {V1: 2, V2: 1}, {V1: 2, V2: 4}, {V1: 2, V2: 2}, {V1: 4, V2: 3}, {V1: 4, V2: 1}, {V1: 4, V2: 4}, {V1: 4, V2: 2}, {V1: 3, V2: 3}, {V1: 3, V2: 1}, {V1: 3, V2: 4}, {V1: 3, V2: 2}, {V1: 1, V2: 3}, {V1: 1, V2: 1}, {V1: 1, V2: 4}, {V1: 1, V2: 2}, ]); }); it('should solve markov according to the list in order when random=0', function() { let solver = new Solver({}); solver.declRange('V1', 1, 4, { valtype: 'markov', legend: [2, 4, 3, 1], expandVectorsWith: 1, random() { return 0; }, // causes first element in legend to be picked }); solver.declRange('V2', 1, 4, { valtype: 'markov', legend: [3, 1, 4, 2], expandVectorsWith: 1, random() { return 0; }, // causes first element in legend to be picked }); solver.gt('V1', 0); solver.gt('V2', 0); let solutions = solver.solve(); expect(countSolutions(solver), 'all solutions').to.equal(16); expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 2, V2: 3}, {V1: 2, V2: 1}, {V1: 2, V2: 4}, {V1: 2, V2: 2}, {V1: 4, V2: 3}, {V1: 4, V2: 1}, {V1: 4, V2: 4}, {V1: 4, V2: 2}, {V1: 3, V2: 3}, {V1: 3, V2: 1}, {V1: 3, V2: 4}, {V1: 3, V2: 2}, {V1: 1, V2: 3}, {V1: 1, V2: 1}, {V1: 1, V2: 4}, {V1: 1, V2: 2}, ]); }); it('should call the list if it is a function', function() { function space_solutionFor(space, varNames) { let result = {}; for (let varIndex = 0; varIndex < varNames.length; varIndex++) { let varName = varNames[varIndex]; result[varName] = space_getVarSolveState(space, varIndex); } return result; } function listCallback(space, varName) { let solution = space_solutionFor(space, ['STATE', 'V1', 'V2']); if (solution['STATE'] === 5) { if (varName === 'V1') { return [2, 4, 3, 1]; } else if (varName === 'V2') { if (solution['V1'] > 0) { return [3, 1, 4, 2]; } } } return [1, 2, 3, 4]; } let solver = new Solver({}); solver.declRange('STATE', 0, 10); solver.declRange('V1', 1, 4, { valtype: 'list', list: listCallback, }); solver.declRange('V2', 1, 4, { valtype: 'list', list: listCallback, }); solver.eq('STATE', 5); solver.gt('V1', 0); solver.gt('V2', 0); let solutions = solver.solve(); expect(countSolutions(solver)).to.equal(16); expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 2, V2: 3, STATE: 5}, {V1: 2, V2: 1, STATE: 5}, {V1: 2, V2: 4, STATE: 5}, {V1: 2, V2: 2, STATE: 5}, {V1: 4, V2: 3, STATE: 5}, {V1: 4, V2: 1, STATE: 5}, {V1: 4, V2: 4, STATE: 5}, {V1: 4, V2: 2, STATE: 5}, {V1: 3, V2: 3, STATE: 5}, {V1: 3, V2: 1, STATE: 5}, {V1: 3, V2: 4, STATE: 5}, {V1: 3, V2: 2, STATE: 5}, {V1: 1, V2: 3, STATE: 5}, {V1: 1, V2: 1, STATE: 5}, {V1: 1, V2: 4, STATE: 5}, {V1: 1, V2: 2, STATE: 5}, ]); }); }); describe('list values trump domain values', function() { it('should ignore values in the domain that are not in the list', function() { let solver = new Solver({}); solver.declRange('V1', 0, 5, { valtype: 'list', list: [0, 3, 4], }); solver.gt('V1', 0); let solutions = solver.solve(); expect(countSolutions(solver)).to.equal(2); // list.length expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 3}, {V1: 4}, ]); }); it('should not solve when the list contains no values in the domain', function() { let solver = new Solver({}); solver.declRange('V1', 0, 10, { valtype: 'list', list: [0, 15], }); solver.gt('V1', 0); solver.solve(); expect(countSolutions(solver)).to.equal(0); }); it('should use the fallback when the list contains no values in the domain', function() { let solver = new Solver({}); solver.declRange('V1', 0, 5, { valtype: 'list', list: [0, 15], fallback: { valtype: 'max', }, }); solver.gt('V1', 0); let solutions = solver.solve(); // Ok. First the propagators run. this will reduce // V1 to [1, 5] because there's a constraint that // says [0,5]>0 meaning the 0 is removed from the // domain. So [1,5]. Then the propagators run into // a stalemate and a choice has to be made. There // is a value distributor based on a list. This // means a value from the domain to be picked has // to exist in that list. Since the list is [0,15] // and we just eliminated the 0, so we may only // pick 15 except the domain doesn't have that so // it ends up picking no value. That means the // fallback distributor will kick in, which is // "max". Since there are no other constraints it // will solve V1 for all remaining values of it // in descending order (5,4,3,2,1). expect(countSolutions(solver)).to.equal(5); expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 5}, {V1: 4}, {V1: 3}, {V1: 2}, {V1: 1}, ]); }); it('should prioritize the list before applying the fallback', function() { let solver = new Solver({}); solver.declRange('V1', 0, 5, { valtype: 'list', list: [3, 0, 1, 5], fallback: { valtype: 'max', }, }); solver.gt('V1', 0); let solutions = solver.solve(); expect(countSolutions(solver)).to.equal(5); expect(stripAnonVarsFromArrays(solutions)).to.eql([ {V1: 3}, {V1: 1}, {V1: 5}, {V1: 4}, {V1: 2}, ]); }); it('should still be able to reject if fallback fails too', function() { let solver = new Solver({}); solver.declRange('V1', 0, 5, { valtype: 'list', list: [3, 0, 1, 15, 5], fallback: { valtype: 'max', }, }); solver.gt('V1', 6); solver.solve(); // original domain contains 0~5 but constraint requires >6 // list contains 15 but since domain doesnt thats irrelevant expect(countSolutions(solver)).to.equal(0); }); }); });