diff --git a/python/lectures.py b/python/lectures.py new file mode 100644 index 0000000000..e69402270e --- /dev/null +++ b/python/lectures.py @@ -0,0 +1,133 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + Lectures problem in Google CP Solver. + + Biggs: Discrete Mathematics (2nd ed), page 187. + ''' + Suppose we wish to schedule six one-hour lectures, v1, v2, v3, v4, v5, v6. + Among the the potential audience there are people who wish to hear both + + - v1 and v2 + - v1 and v4 + - v3 and v5 + - v2 and v6 + - v4 and v5 + - v5 and v6 + - v1 and v6 + + How many hours are necessary in order that the lectures can be given + without clashes? + ''' + + Compare with the following models: + * MiniZinc: http://www.hakank.org/minizinc/lectures.mzn + * SICstus: http://hakank.org/sicstus/lectures.pl + * ECLiPSe: http://hakank.org/eclipse/lectures.ecl + * Gecode: http://hakank.org/gecode/lectures.cpp + + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Lectures') + + # + # data + # + + # + # The schedule requirements: + # lecture a cannot be held at the same time as b + # Note: 1-based + g = [ + [1, 2], + [1, 4], + [3, 5], + [2, 6], + [4, 5], + [5, 6], + [1, 6] + ] + + # number of nodes + n = 6 + + # number of edges + edges = len(g) + + # + # declare variables + # + v = [solver.IntVar(0, n-1, 'v[%i]' % i) for i in range(n)] + + # maximum color, to minimize + # Note: since Python is 0-based, the + # number of colors is +1 + max_c = solver.IntVar(0, n-1, 'max_c') + + + # + # constraints + # + solver.Add(max_c == solver.Max(v)) + + # ensure that there are no clashes + # also, adjust to 0-base + for i in range(edges): + solver.Add(v[g[i][0]-1] != v[g[i][1]-1]) + + # symmetry breaking: + # - v0 has the color 0, + # - v1 has either color 0 or 1 + solver.Add(v[0] == 0) + solver.Add(v[1] <= 1) + + # objective + objective = solver.Minimize(max_c, 1) + + # + # solution and search + # + db = solver.Phase(v, + solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + solver.ASSIGN_CENTER_VALUE) + + solver.NewSearch(db, [objective]) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + print 'max_c:', max_c.Value()+1, 'colors' + print 'v:', [v[i].Value() for i in range(n)] + print + + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/marathon2.py b/python/marathon2.py new file mode 100644 index 0000000000..6602df57e6 --- /dev/null +++ b/python/marathon2.py @@ -0,0 +1,138 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + Marathon puzzle in Google CP Solver. + + From Xpress example + http://www.dashoptimization.com/home/cgi-bin/example.pl?id=mosel_puzzle_5_3 + ''' + Dominique, Ignace, Naren, Olivier, Philippe, and Pascal + have arrived as the first six at the Paris marathon. + Reconstruct their arrival order from the following + information: + a) Olivier has not arrived last + b) Dominique, Pascal and Ignace have arrived before Naren + and Olivier + c) Dominique who was third last year has improved this year. + d) Philippe is among the first four. + e) Ignace has arrived neither in second nor third position. + f) Pascal has beaten Naren by three positions. + g) Neither Ignace nor Dominique are on the fourth position. + + (c) 2002 Dash Associates + author: S. Heipcke, Mar. 2002 + ''' + + Compare with the following models: + * MiniZinc: http://www.hakank.org/minizinc/marathon2.mzn + * SICStus Prolog: http://www.hakank.org/sicstus/marathon2.pl + * ECLiPSe: http://hakank.org/eclipse/marathon2.ecl + * Gecode: http://hakank.org/gecode/marathon2.cpp + + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Marathon') + + # + # data + # + n = 6 + + runners_str = ['Dominique', 'Ignace', 'Naren', + 'Olivier', 'Philippe', 'Pascal'] + + # + # declare variables + # + runners = [solver.IntVar(1, n, 'runners[%i]' % i) for i in range(n)] + Dominique, Ignace, Naren, Olivier, Philippe, Pascal = runners + + + + + # + # constraints + # + solver.Add(solver.AllDifferent(runners, True)) + + # a: Olivier not last + solver.Add(Olivier != n) + + # b: Dominique, Pascal and Ignace before Naren and Olivier + solver.Add(Dominique < Naren) + solver.Add(Dominique < Olivier) + solver.Add(Pascal < Naren) + solver.Add(Pascal < Olivier) + solver.Add(Ignace < Naren) + solver.Add(Ignace < Olivier) + + # c: Dominique better than third + solver.Add(Dominique < 3) + + # d: Philippe is among the first four + solver.Add(Philippe <= 4) + + # e: Ignace neither second nor third + solver.Add(Ignace != 2) + solver.Add(Ignace != 3) + + # f: Pascal three places earlier than Naren + solver.Add(Pascal + 3 == Naren) + + # g: Neither Ignace nor Dominique on fourth position + solver.Add(Ignace != 4) + solver.Add(Dominique != 4) + + + # + # solution and search + # + db = solver.Phase(runners, + solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + solver.ASSIGN_CENTER_VALUE) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + runners_val = [runners[i].Value() for i in range(n)] + print 'runners:', runners_val + print "Places:" + for i in range(1, n+1): + for j in range(n): + if runners_val[j] == i: + print "%i: %s" % (i, runners_str[j]) + print + + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/mr_smith.py b/python/mr_smith.py new file mode 100644 index 0000000000..385819cadb --- /dev/null +++ b/python/mr_smith.py @@ -0,0 +1,125 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + Mr Smith in Google CP Solver. + + From an IF Prolog example (http://www.ifcomputer.de/) + ''' + The Smith family and their three children want to pay a visit but they + do not all have the time to do so. Following are few hints who will go + and who will not: + o If Mr Smith comes, his wife will come too. + o At least one of their two sons Matt and John will come. + o Either Mrs Smith or Tim will come, but not both. + o Either Tim and John will come, or neither will come. + o If Matt comes, then John and his father will + also come. + ''' + + The answer should be: + Mr_Smith_comes = 0 + Mrs_Smith_comes = 0 + Matt_comes = 0 + John_comes = 1 + Tim_comes = 1 + + Compare with the following models: + * ECLiPSe: http://www.hakank.org/eclipse/mr_smith.ecl + * SICStus Prolog: http://www.hakank.org/sicstus/mr_smith.pl + * Gecode: http://www.hakank.org/gecode/mr_smith.cpp + * MiniZinc: http://www.hakank.org/minizinc/mr_smith.mzn + + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Mr Smith problem') + + # + # data + # + n = 5 + + + # + # declare variables + # + x = [solver.IntVar(0, 1, 'x[%i]' % i) for i in range(n)] + Mr_Smith, Mrs_Smith, Matt, John, Tim = x + + + # + # constraints + # + + # + # I've kept the MiniZinc constraints for clarity + # and debugging. + # + + # If Mr Smith comes then his wife will come too. + # (Mr_Smith -> Mrs_Smith) + solver.Add(Mr_Smith-Mrs_Smith <= 0) + + # At least one of their two sons Matt and John will come. + # (Matt \/ John) + solver.Add(Matt+John >= 1) + + # Either Mrs Smith or Tim will come but not both. + # bool2int(Mrs_Smith) + bool2int(Tim) = 1 /\ + # (Mrs_Smith xor Tim) + solver.Add(Mrs_Smith + Tim == 1) + + # Either Tim and John will come or neither will come. + # (Tim = John) + solver.Add(Tim == John) + + # If Matt comes /\ then John and his father will also come. + # (Matt -> (John /\ Mr_Smith)) + solver.Add(Matt - (John*Mr_Smith) <= 0) + + + # + # solution and search + # + db = solver.Phase(x, + solver.INT_VAR_DEFAULT, + solver.INT_VALUE_DEFAULT) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + print 'x:', [x[i].Value() for i in range(n)] + + print + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/olympic.py b/python/olympic.py new file mode 100644 index 0000000000..66e26ec9b6 --- /dev/null +++ b/python/olympic.py @@ -0,0 +1,116 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + Olympic puzzle in Google CP Solver. + + Benchmark for Prolog (BProlog) + ''' + File : olympic.pl + Author : Neng-Fa ZHOU + Date : 1993 + + Purpose: solve a puzzle taken from Olympic Arithmetic Contest + + Given ten variables with the following configuration: + + X7 X8 X9 X10 + + X4 X5 X6 + + X2 X3 + + X1 + + We already know that X1 is equal to 3 and want to assign each variable + with a different integer from {1,2,...,10} such that for any three + variables + Xi Xj + + Xk + the following constraint is satisfied: + + |Xi-Xj| = Xk + ''' + + Compare with the following models: + * MiniZinc: http://www.hakank.org/minizinc/olympic.mzn + * SICStus Prolog: http://www.hakank.org/sicstus/olympic.pl + * ECLiPSe: http://hakank.org/eclipse/olympic.ecl + * Gecode: http://hakank.org/gecode/olympic.cpp + + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + +def minus(solver, x, y, z): + solver.Add(z == abs(x - y)) + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Olympic') + + # + # data + # + n = 10 + + # + # declare variables + # + Vars = [solver.IntVar(1, n, 'Vars[%i]' % i) for i in range(n)] + X1,X2,X3,X4,X5,X6,X7,X8,X9,X10 = Vars + + # + # constraints + # + solver.Add(solver.AllDifferent(Vars, True)) + + solver.Add(X1 == 3) + minus(solver, X2, X3, X1) + minus(solver, X4, X5, X2) + minus(solver, X5, X6, X3) + minus(solver, X7, X8, X4) + minus(solver, X8, X9, X5) + minus(solver, X9, X10, X6) + + # + # solution and search + # + db = solver.Phase(Vars, + solver.INT_VAR_SIMPLE, + solver.INT_VALUE_DEFAULT) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + print 'Vars:', [Vars[i].Value() for i in range(n)] + + print + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/organize_day.py b/python/organize_day.py new file mode 100644 index 0000000000..2d0c863f8b --- /dev/null +++ b/python/organize_day.py @@ -0,0 +1,120 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + Organizing a day in Google CP Solver. + + Simple scheduling problem. + + Problem formulation from ECLiPSe: + Slides on (Finite Domain) Constraint Logic Programming, page 38f + http://eclipse-clp.org/reports/eclipse.ppt + + + Compare with the following models: + * MiniZinc: http://www.hakank.org/minizinc/organize_day.mzn + * Comet: http://www.hakank.org/comet/organize_day.co + * Gecode: http://hakank.org/gecode/organize_day.cpp + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + +# +# No overlapping of tasks s1 and s2 +# +def no_overlap(solver, s1, d1, s2, d2): + b1 = solver.MakeIsLessOrEqualVar(s1 + d1, s2) # s1 + d1 <= s2 + b2 = solver.MakeIsLessOrEqualVar(s2 + d2, s1) # s2 + d2 <= s1 + solver.Add(b1 + b2 >= 1) + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Organizing a day') + + # + # data + # + n = 4 + + tasks = range(n) + work, mail, shop, bank = tasks + durations = [4,1,2,1] + + # task [i,0] must be finished before task [i,1] + before_tasks = [ + [bank, shop], + [mail, work] + ] + + # the valid times of the day + begin = 9 + end = 17 + + # + # declare variables + # + begins = [solver.IntVar(begin, end, 'begins[%i]% % i') for i in tasks] + ends = [solver.IntVar(begin, end, 'ends[%i]% % i') for i in tasks] + + # + # constraints + # + for i in tasks: + solver.Add(ends[i] == begins[i] + durations[i]) + + for i in tasks: + for j in tasks: + if i < j: + no_overlap(solver, + begins[i], durations[i], + begins[j], durations[j]) + + # specific constraints + for (before, after) in before_tasks: + solver.Add(ends[before] <= begins[after]) + + solver.Add(begins[work] >= 11) + + + # + # solution and search + # + db = solver.Phase(begins + ends, + solver.INT_VAR_DEFAULT, + solver.INT_VALUE_DEFAULT) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + print 'begins:', [begins[i].Value() for i in tasks] + print 'ends:', [ends[i].Value() for i in tasks] + print + + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/p_median.py b/python/p_median.py new file mode 100644 index 0000000000..fdb3b1cc8d --- /dev/null +++ b/python/p_median.py @@ -0,0 +1,130 @@ +# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + + P-median problem in Google CP Solver. + + Model and data from the OPL Manual, which describes the problem: + ''' + The P-Median problem is a well known problem in Operations Research. + The problem can be stated very simply, like this: given a set of customers + with known amounts of demand, a set of candidate locations for warehouses, + and the distance between each pair of customer-warehouse, choose P + warehouses to open that minimize the demand-weighted distance of serving + all customers from those P warehouses. + ''' + + Compare with the following models: + * MiniZinc: http://hakank.org/minizinc/p_median.mzn + * Comet: http://hakank.org/comet/p_median.co + + This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) + Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/ +""" + +import sys +from constraint_solver import pywrapcp + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('P-median problem') + + # + # data + # + p = 2 + + num_customers = 4 + customers = range(num_customers) + Albert, Bob, Chris, Daniel = customers + num_warehouses = 3 + warehouses = range(num_warehouses) + Santa_Clara, San_Jose, Berkeley = warehouses + + demand = [100,80,80,70] + distance = [ + [ 2, 10, 50], + [ 2, 10, 52], + [50, 60, 3], + [40, 60, 1] + ] + + # + # declare variables + # + open = [solver.IntVar(warehouses, 'open[%i]% % i') + for w in warehouses] + ship = {} + for c in customers: + for w in warehouses: + ship[c,w] = solver.IntVar(0, 1,'ship[%i,%i]' % (c,w)) + ship_flat = [ship[c,w] + for c in customers + for w in warehouses] + + z = solver.IntVar(0, 1000, 'z') + + # + # constraints + # + z_sum = solver.Sum([demand[c]*distance[c][w]*ship[c,w] + for c in customers + for w in warehouses]) + solver.Add(z == z_sum) + + for c in customers: + s = solver.Sum([ship[c,w] + for w in warehouses]) + solver.Add(s == 1) + + solver.Add(solver.Sum(open) == p) + + for c in customers: + for w in warehouses: + solver.Add(ship[c,w] <= open[w]) + + # objective + objective = solver.Minimize(z, 1) + + # + # solution and search + # + db = solver.Phase(open + ship_flat, + solver.INT_VAR_DEFAULT, + solver.INT_VALUE_DEFAULT) + + solver.NewSearch(db, [objective]) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + print "z:", z.Value() + print 'open:', [open[w].Value() for w in warehouses] + for c in customers: + for w in warehouses: + print ship[c,w].Value(), + print + print + + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main()