Numberjack Modelling, reformulation, and solving Barry Hurley barry.hurley@insight-centre.org ModRef 2014 Outline Introduction to Numberjack Modelling Constructs solve() and friends Example Application and Demos Summary Insight Centre for Data Analytics September 8, 2014 Slide 2 Numberjack Overview What is Numberjack? • A platform for combinatorial optimization • Open source project (Github, LGPL) • Python • Common platform for diverse paradigms (CP, SAT, MIP) • Fast backend solvers Insight Centre for Data Analytics September 8, 2014 Slide 3 CP, SAT, MIP: choice is good! Numberjack • Write a single model in Python • Numberjack constructs: union of SAT, MIP, and CP • Encoded depending on the choice of solver Operationally different • Algorithms are different • CP: Constraint propagation + Search • SAT: Unit propagation + Clause Learning + Search • MIP: Linear relaxation + Cutting planes + Branch & Bound • Encodings matter Insight Centre for Data Analytics September 8, 2014 Slide 4 Backend Solvers Available CP Solvers MIP Solvers • Mistral v1 & v2 • IBM ILOG CPLEX • Toulbar2 • Gurobi SAT Solvers • MiniSat • Walksat • Lingeling • CryptoMiniSat • Glucose • SCIP • Through COIN-OR Open Solver Interface: • • • • • • • COIN-OR CBC COIN-OR CLP COIN-OR DyLP COIN-OR SYMPHONY COIN-OR Volume Algorithm GNU LP Toolkit Soplex • . . . any CNF solver Insight Centre for Data Analytics September 8, 2014 Slide 5 Installation Python Package Index (PyPI) pip install Numberjack Insight Centre for Data Analytics September 8, 2014 Slide 6 Section 2 Modelling Constructs Insight Centre for Data Analytics September 8, 2014 Slide 7 Variables in Numberjack Different Variable constructors Constructor Variable() Variable(’x’) Variable(N) Variable(N, ’x’) Variable(l,u) Variable(l,u, ’x’) Variable(list) Variable(list, ’x’) Insight Centre for Data Analytics Description Boolean variable Boolean variable called ’x’ Variable in the domain of [0 . . . N − 1] Variable in the domain of [0 . . . N − 1] called ’x’ Variable in the domain of [l . . . u] Variable in the domain of [l . . . u] called ’x’ Variable with domain specified as a list Variable with domain as a list called ’x’ September 8, 2014 Slide 8 More Variables in Numberjack Similarily for arrays and matrices Constructor VarArray(N) VarArray(N, u) VarArray(N, l, u) .. . Description Array of N Boolean variables Array of N variables with domains [0 . . . u − 1] Array of N variables with domains [l . . . u] Matrix(N, M) Matrix(N, M, u) Matrix(N, M, l, u) N × M matrix of Boolean variables N × M matrix of variables with domains [0 . . . u − 1] N × M matrix of variables with domains [l . . . u] Insight Centre for Data Analytics September 8, 2014 Slide 9 Concise Models VarArray and Matrix allow for concise modelling. Slices Given a 9 × 9 Sudoku matrix, we could get a 3 × 3 cell: AllDiff( matrix[x:x+3, y:y+3] ) Rows and Columns for row in matrix.row: m += AllDiff(row) for col in matrix.col: m += AllDiff(col) Insight Centre for Data Analytics September 8, 2014 Slide 10 Constraints in Numberjack Constraints can be specified in a number of ways Simply by arithmetic operators on variables: x y x x > y <= z != z + y == z Insight Centre for Data Analytics x + 4 > z * 3 z == (x < y) z <= (x < y) (x == y) != (a == b) September 8, 2014 Slide 11 Global Constraints v == Max([x,y,z]) Disjunction([x < y, y > z, a != b]) AllDiff([x,y,z]) Sum([a,b,c,d]) >= e Sum([a,b,c,d], [2, 1, 0.5, 3]) == e # Weighted Sum Sum([2∗a, b, 0.5∗c, 3∗d]) == e # Weighted Sum LessLex(VarArray(5, 5), VarArray(5, 5)) LeqLex(VarArray(5, 5), VarArray(5, 5)) Gcc(VarArray(4, 1, 5), {1: [1, 3], 2: [1, 2]}) Element(VarArray(10, 1, 10), Variable(10)) myarray[myvariable] # Element Minimize(2∗x + 3∗y) Maximize(objective) PostUnary, PostBinary, PostNary, ... # Cost functions Insight Centre for Data Analytics September 8, 2014 Slide 12 Custom Constraints Easy to define your own custom constraints for modelling purposes and add custom decompositions for solvers which don’t support it. Example class MyAllDiff(Expression): def decompose(self): return [var1 != var2 for var1, var2 in pair_of(self.children)] class NoOverlap(Predicate): def decompose(self): task_i, task_j = self.children return [((task_i + task_i.duration) <= task_j) | ((task_j + task_j.duration) <= task_i)] Insight Centre for Data Analytics September 8, 2014 Slide 13 Custom Decompositions Override the default decomposition simply by defining your own method: Example def decompose_AllDiff(self): return [var1 != var2 for var1, var2 in pair_of(self.children)] Insight Centre for Data Analytics September 8, 2014 Slide 14 Section 3 solve() and friends Insight Centre for Data Analytics September 8, 2014 Slide 15 One Model, Many Solvers Constraint Solvers mistral = model.load("Mistral") toulbar = model.load("Toulbar2") MIP Solvers gurobi = model.load("Gurobi") cplex = model.load("CPLEX") SAT Solvers minisat = model.load("MiniSat") walksat = model.load("Walksat") lingeling = model.load("Lingeling") Insight Centre for Data Analytics September 8, 2014 Slide 16 Customizable SAT Encoding Change the default encoding globally model.load(solver, encoding=...) direct support p cnf 1 2 3 -1 -2 -1 -3 -2 -3 4 5 6 -4 -5 -4 -6 -5 -6 7 8 9 -7 -8 -7 -9 -8 -9 -1 -4 -2 -5 -3 -6 -1 -7 -2 -8 -3 -9 -4 -7 -5 -8 -6 -9 p cnf 9 30 1 2 3 0 -1 -2 0 -1 -3 0 -2 -3 0 4 5 6 0 -4 -5 0 -4 -6 0 -5 -6 0 7 8 9 0 -7 -8 0 -7 -9 0 -8 -9 0 5 6 -1 0 4 6 -2 0 4 5 -3 0 2 3 -4 0 1 3 -5 0 1 2 -6 0 8 9 -1 0 7 9 -2 0 ... 9 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Insight Centre for Data Analytics order regular p cnf 6 12 -1 2 0 -3 4 0 -5 6 0 -1 -3 0 1 3 -2 -4 0 2 4 0 -1 -5 0 1 5 -2 -6 0 2 6 0 -3 -5 0 3 5 -4 -6 0 4 6 0 p cnf 15 27 1 2 3 0 -4 5 0 -1 4 0 -2 -4 0 -2 5 0 -3 -5 0 6 7 8 0 -9 10 0 -6 9 0 -7 -9 0 -7 10 0 -8 -10 0 11 12 13 0 -14 15 0 -11 14 0 -12 -14 0 -12 15 0 -13 -15 0 -1 -6 0 -2 -7 0 ... September 8, 2014 Slide 17 Customizable SAT Encoding Pure direct/sparse domain encoding x = Variable(3) x.encoding = EncodingConfiguration(direct=True, order=False) x0 ∨ x1 ∨ x2 ¬x0 ∨ ¬x1 ¬x0 ∨ ¬x2 ¬x1 ∨ ¬x2 Regular domain encoding y0 ∨ y1 ∨ y 2 ¬y≤0 ∨ y≤1 y = Variable(3) y.encoding = EncodingConfiguration(direct=True, order=True) ¬y0 ∨ y≤0 ¬y1 ∨ ¬y≤0 ¬y1 ∨ y≤1 ¬y2 ∨ ¬y≤1 Full regular domain encoding z = Variable(3) z.encoding = EncodingConfiguration(direct=False, order=True) Insight Centre for Data Analytics ¬z≤0 ∨ z≤1 September 8, 2014 Slide 18 Customizable SAT Encoding Per constraint neq.encoding = EncodingConfiguration( conflict=False, support=True, order=False) ¬x0 ∨ y1 ∨ y2 ¬x1 ∨ y0 ∨ y2 ¬x2 ∨ y0 ∨ y1 ... Multiple encodings con = AllDiff(variables) con.encoding = EncodingConfiguration(alldiff_encoding= AllDiffEncoding.PairwiseDecomp | AllDiffEncoding.LadderAMO | AllDiffEncoding.PigeonHole) Named presets con.encoding = NJEncodings[’pairwise_ladder_pigeon’] Insight Centre for Data Analytics September 8, 2014 Slide 19 Configuring the Solver # Search strategies (solver specific) mistral.setHeuristic("DomainOverWDegree", "Lex") mistral.setHeuristic("Impact") # Set Limits solver.setTimeLimit(60) # Seconds solver.setNodeLimit(1000000) # Solve solver.solve() # Solve using custom restart parameters mistral.solveAndRestart(GEOMETRIC, 64, 1.3) mistral.solveAndRestart(LUBY, 1000) # Find all solutions solver.getNextSolution() # Feasibility? solver.is_sat() solver.is_opt() solver.is_unsat() Insight Centre for Data Analytics September 8, 2014 Slide 20 Custom Search: Full binary DFS solver.startNewSearch() while not proven_infeasibility: if solver.propagate(): # left branch x = variableselection(variables) if x is None: # Complete, no more variables to assign return solver.get_solution() v = x.get_min() solver.save() solver.post(x == v) else: # right branch proven_infeasibility = not solver.undo() if not proven_infeasibility: # Invert the previous decision, i.e. x != v solver.deduce() Insight Centre for Data Analytics September 8, 2014 Slide 21 Model Output Output LP model cplex = model.load("CPLEX") cplex.output_lp("mymodel.lp") Output MPS model gurobi = model.load("Gurobi") gurobi.output_mps("mymodel.mps") Output CNF model minisat = model.load("MiniSat") minisat.output_cnf("mymodel.cnf") Insight Centre for Data Analytics September 8, 2014 Slide 22 External Input Formats XCSP from Numberjack.XCSP import XCSPParser parser = XCSPParser(filename) model, variables = parser.model, parser.variables mzn/fzn # Just run Numberjack solvers directly mzn_numberjack model.mzn data.dzn fzn_numberjack flattened.fzn # Translate to a Numberjack/Python file mzn2py model.mzn data.dzn > model.py Other custom parsers are easy in Python cnf, pls, uai, . . . Insight Centre for Data Analytics ‘import antigravity’ September 8, 2014 Slide 23 Section 4 Example Application and Demos Insight Centre for Data Analytics September 8, 2014 Slide 24 Optical Network Design Insight Centre for Data Analytics September 8, 2014 Slide 25 Demo: Map Colouring Insight Centre for Data Analytics September 8, 2014 Slide 26 Demo: Controlling Search Demo: controlling search Insight Centre for Data Analytics September 8, 2014 Slide 27 Summary What is Numberjack? • • • • • • A Python platform for combinatorial optimization Common platform for diverse paradigms (CP, SAT, MIP) Practical, easy to use, and intuitive Highly customizable encodings Fast backend solvers Open source (Github, LGPL) http://numberjack.ucc.ie Insight Centre for Data Analytics September 8, 2014 Slide 28 Acknowledgements This work is supported by Science Foundation Ireland (SFI) Grant 10/IN.1/I3032 and FP7 FET-Open Grant 284715. The Insight Centre for Data Analytics is supported by SFI Grant SFI/12/RC/2289. Insight Centre for Data Analytics September 8, 2014 Slide 29