From 0a822b661774791177704dd188104ba0fe459c8d Mon Sep 17 00:00:00 2001 From: acco32 Date: Tue, 3 Jul 2018 22:21:35 -0700 Subject: [PATCH] Add examples for C# --- .gitignore | 3 + examples/dotnet/OrTools.sln | 34 ++ examples/dotnet/README.md | 21 + examples/dotnet/csharp/3_jugs_regular.cs | 293 +++++++++++++ examples/dotnet/csharp/TaskScheduling.cs | 201 +++++++++ examples/dotnet/csharp/a_puzzle.cs | 263 ++++++++++++ examples/dotnet/csharp/a_round_of_golf.cs | 183 ++++++++ examples/dotnet/csharp/all_interval.cs | 100 +++++ .../dotnet/csharp/alldifferent_except_0.cs | 103 +++++ examples/dotnet/csharp/assignment.cs | 137 ++++++ examples/dotnet/csharp/broken_weights.cs | 167 ++++++++ examples/dotnet/csharp/bus_schedule.cs | 131 ++++++ examples/dotnet/csharp/circuit.cs | 118 +++++ examples/dotnet/csharp/circuit2.cs | 129 ++++++ examples/dotnet/csharp/code_samples_sat.cs | 404 ++++++++++++++++++ examples/dotnet/csharp/coins3.cs | 114 +++++ examples/dotnet/csharp/coins_grid.cs | 113 +++++ .../dotnet/csharp/combinatorial_auction2.cs | 118 +++++ examples/dotnet/csharp/contiguity_regular.cs | 209 +++++++++ .../dotnet/csharp/contiguity_transition.cs | 123 ++++++ examples/dotnet/csharp/costas_array.cs | 181 ++++++++ examples/dotnet/csharp/covering_opl.cs | 128 ++++++ examples/dotnet/csharp/crew.cs | 272 ++++++++++++ examples/dotnet/csharp/crossword.cs | 188 ++++++++ examples/dotnet/csharp/crypta.cs | 119 ++++++ examples/dotnet/csharp/crypto.cs | 152 +++++++ examples/dotnet/csharp/cscvrptw.cs | 355 +++++++++++++++ examples/dotnet/csharp/csflow.cs | 108 +++++ examples/dotnet/csharp/csfz.cs | 95 ++++ .../dotnet/csharp/csintegerprogramming.cs | 124 ++++++ examples/dotnet/csharp/csjobshop.cs | 229 ++++++++++ examples/dotnet/csharp/csknapsack.cs | 49 +++ examples/dotnet/csharp/cslinearprogramming.cs | 176 ++++++++ examples/dotnet/csharp/csls_api.cs | 192 +++++++++ examples/dotnet/csharp/csrabbitspheasants.cs | 73 ++++ examples/dotnet/csharp/cstsp.cs | 120 ++++++ .../dotnet/csharp/curious_set_of_integers.cs | 122 ++++++ examples/dotnet/csharp/debruijn.cs | 202 +++++++++ examples/dotnet/csharp/diet.cs | 101 +++++ examples/dotnet/csharp/discrete_tomography.cs | 216 ++++++++++ .../dotnet/csharp/divisible_by_9_through_1.cs | 192 +++++++++ examples/dotnet/csharp/dudeney.cs | 122 ++++++ examples/dotnet/csharp/einav_puzzle2.cs | 228 ++++++++++ examples/dotnet/csharp/eq10.cs | 112 +++++ examples/dotnet/csharp/eq20.cs | 124 ++++++ examples/dotnet/csharp/examples.csproj | 15 + examples/dotnet/csharp/fill_a_pix.cs | 248 +++++++++++ examples/dotnet/csharp/furniture_moving.cs | 180 ++++++++ .../csharp/furniture_moving_intervals.cs | 150 +++++++ examples/dotnet/csharp/futoshiki.cs | 200 +++++++++ examples/dotnet/csharp/gate_scheduling_sat.cs | 154 +++++++ examples/dotnet/csharp/golomb_ruler.cs | 128 ++++++ examples/dotnet/csharp/grocery.cs | 112 +++++ examples/dotnet/csharp/hidato_table.cs | 287 +++++++++++++ examples/dotnet/csharp/jobshop_ft06_sat.cs | 141 ++++++ examples/dotnet/csharp/just_forgotten.cs | 129 ++++++ examples/dotnet/csharp/kakuro.cs | 219 ++++++++++ examples/dotnet/csharp/kenken2.cs | 241 +++++++++++ examples/dotnet/csharp/killer_sudoku.cs | 240 +++++++++++ examples/dotnet/csharp/labeled_dice.cs | 184 ++++++++ examples/dotnet/csharp/langford.cs | 113 +++++ examples/dotnet/csharp/least_diff.cs | 91 ++++ examples/dotnet/csharp/lectures.cs | 193 +++++++++ examples/dotnet/csharp/magic_sequence.cs | 112 +++++ examples/dotnet/csharp/magic_square.cs | 140 ++++++ .../dotnet/csharp/magic_square_and_cards.cs | 131 ++++++ examples/dotnet/csharp/map.cs | 96 +++++ examples/dotnet/csharp/map2.cs | 107 +++++ examples/dotnet/csharp/marathon2.cs | 144 +++++++ examples/dotnet/csharp/max_flow_taha.cs | 170 ++++++++ examples/dotnet/csharp/max_flow_winston1.cs | 165 +++++++ examples/dotnet/csharp/minesweeper.cs | 232 ++++++++++ examples/dotnet/csharp/mr_smith.cs | 136 ++++++ examples/dotnet/csharp/nontransitive_dice.cs | 210 +++++++++ examples/dotnet/csharp/nqueens.cs | 119 ++++++ .../dotnet/csharp/nurse_rostering_regular.cs | 323 ++++++++++++++ .../csharp/nurse_rostering_transition.cs | 286 +++++++++++++ examples/dotnet/csharp/nurses_sat.cs | 219 ++++++++++ examples/dotnet/csharp/olympic.cs | 137 ++++++ examples/dotnet/csharp/organize_day.cs | 148 +++++++ .../dotnet/csharp/organize_day_intervals.cs | 124 ++++++ examples/dotnet/csharp/p_median.cs | 137 ++++++ examples/dotnet/csharp/pandigital_numbers.cs | 193 +++++++++ examples/dotnet/csharp/partition.cs | 130 ++++++ .../dotnet/csharp/perfect_square_sequence.cs | 144 +++++++ examples/dotnet/csharp/photo_problem.cs | 178 ++++++++ examples/dotnet/csharp/place_number_puzzle.cs | 141 ++++++ .../dotnet/csharp/post_office_problem2.cs | 148 +++++++ .../dotnet/csharp/quasigroup_completion.cs | 229 ++++++++++ examples/dotnet/csharp/regex.cs | 202 +++++++++ examples/dotnet/csharp/rogo2.cs | 356 +++++++++++++++ examples/dotnet/csharp/scheduling_speakers.cs | 99 +++++ examples/dotnet/csharp/secret_santa.cs | 129 ++++++ examples/dotnet/csharp/secret_santa2.cs | 248 +++++++++++ examples/dotnet/csharp/send_more_money.cs | 82 ++++ examples/dotnet/csharp/send_more_money2.cs | 94 ++++ examples/dotnet/csharp/send_most_money.cs | 113 +++++ examples/dotnet/csharp/seseman.cs | 122 ++++++ examples/dotnet/csharp/set_covering.cs | 109 +++++ examples/dotnet/csharp/set_covering2.cs | 115 +++++ examples/dotnet/csharp/set_covering3.cs | 131 ++++++ examples/dotnet/csharp/set_covering4.cs | 134 ++++++ .../dotnet/csharp/set_covering_deployment.cs | 149 +++++++ examples/dotnet/csharp/set_covering_skiena.cs | 126 ++++++ examples/dotnet/csharp/set_partition.cs | 204 +++++++++ examples/dotnet/csharp/sicherman_dice.cs | 145 +++++++ examples/dotnet/csharp/ski_assignment.cs | 122 ++++++ examples/dotnet/csharp/slow_scheduling.cs | 284 ++++++++++++ examples/dotnet/csharp/stable_marriage.cs | 232 ++++++++++ examples/dotnet/csharp/strimko2.cs | 133 ++++++ examples/dotnet/csharp/subset_sum.cs | 118 +++++ examples/dotnet/csharp/sudoku.cs | 133 ++++++ examples/dotnet/csharp/survo_puzzle.cs | 238 +++++++++++ examples/dotnet/csharp/techtalk_scheduling.cs | 282 ++++++++++++ examples/dotnet/csharp/to_num.cs | 101 +++++ examples/dotnet/csharp/traffic_lights.cs | 142 ++++++ examples/dotnet/csharp/volsay.cs | 89 ++++ examples/dotnet/csharp/volsay2.cs | 107 +++++ examples/dotnet/csharp/volsay3.cs | 118 +++++ .../dotnet/csharp/wedding_optimal_chart.cs | 215 ++++++++++ examples/dotnet/csharp/who_killed_agatha.cs | 154 +++++++ examples/dotnet/csharp/word_square.cs | 199 +++++++++ examples/dotnet/csharp/xkcd.cs | 77 ++++ examples/dotnet/csharp/young_tableaux.cs | 141 ++++++ examples/dotnet/csharp/zebra.cs | 178 ++++++++ 125 files changed, 19889 insertions(+) create mode 100644 examples/dotnet/OrTools.sln create mode 100644 examples/dotnet/README.md create mode 100644 examples/dotnet/csharp/3_jugs_regular.cs create mode 100644 examples/dotnet/csharp/TaskScheduling.cs create mode 100644 examples/dotnet/csharp/a_puzzle.cs create mode 100644 examples/dotnet/csharp/a_round_of_golf.cs create mode 100644 examples/dotnet/csharp/all_interval.cs create mode 100644 examples/dotnet/csharp/alldifferent_except_0.cs create mode 100644 examples/dotnet/csharp/assignment.cs create mode 100644 examples/dotnet/csharp/broken_weights.cs create mode 100644 examples/dotnet/csharp/bus_schedule.cs create mode 100644 examples/dotnet/csharp/circuit.cs create mode 100644 examples/dotnet/csharp/circuit2.cs create mode 100644 examples/dotnet/csharp/code_samples_sat.cs create mode 100644 examples/dotnet/csharp/coins3.cs create mode 100644 examples/dotnet/csharp/coins_grid.cs create mode 100644 examples/dotnet/csharp/combinatorial_auction2.cs create mode 100644 examples/dotnet/csharp/contiguity_regular.cs create mode 100644 examples/dotnet/csharp/contiguity_transition.cs create mode 100644 examples/dotnet/csharp/costas_array.cs create mode 100644 examples/dotnet/csharp/covering_opl.cs create mode 100644 examples/dotnet/csharp/crew.cs create mode 100644 examples/dotnet/csharp/crossword.cs create mode 100644 examples/dotnet/csharp/crypta.cs create mode 100644 examples/dotnet/csharp/crypto.cs create mode 100644 examples/dotnet/csharp/cscvrptw.cs create mode 100644 examples/dotnet/csharp/csflow.cs create mode 100644 examples/dotnet/csharp/csfz.cs create mode 100644 examples/dotnet/csharp/csintegerprogramming.cs create mode 100644 examples/dotnet/csharp/csjobshop.cs create mode 100644 examples/dotnet/csharp/csknapsack.cs create mode 100644 examples/dotnet/csharp/cslinearprogramming.cs create mode 100644 examples/dotnet/csharp/csls_api.cs create mode 100644 examples/dotnet/csharp/csrabbitspheasants.cs create mode 100644 examples/dotnet/csharp/cstsp.cs create mode 100644 examples/dotnet/csharp/curious_set_of_integers.cs create mode 100644 examples/dotnet/csharp/debruijn.cs create mode 100644 examples/dotnet/csharp/diet.cs create mode 100644 examples/dotnet/csharp/discrete_tomography.cs create mode 100644 examples/dotnet/csharp/divisible_by_9_through_1.cs create mode 100644 examples/dotnet/csharp/dudeney.cs create mode 100644 examples/dotnet/csharp/einav_puzzle2.cs create mode 100644 examples/dotnet/csharp/eq10.cs create mode 100644 examples/dotnet/csharp/eq20.cs create mode 100644 examples/dotnet/csharp/examples.csproj create mode 100644 examples/dotnet/csharp/fill_a_pix.cs create mode 100644 examples/dotnet/csharp/furniture_moving.cs create mode 100644 examples/dotnet/csharp/furniture_moving_intervals.cs create mode 100644 examples/dotnet/csharp/futoshiki.cs create mode 100644 examples/dotnet/csharp/gate_scheduling_sat.cs create mode 100644 examples/dotnet/csharp/golomb_ruler.cs create mode 100644 examples/dotnet/csharp/grocery.cs create mode 100644 examples/dotnet/csharp/hidato_table.cs create mode 100644 examples/dotnet/csharp/jobshop_ft06_sat.cs create mode 100644 examples/dotnet/csharp/just_forgotten.cs create mode 100644 examples/dotnet/csharp/kakuro.cs create mode 100644 examples/dotnet/csharp/kenken2.cs create mode 100644 examples/dotnet/csharp/killer_sudoku.cs create mode 100644 examples/dotnet/csharp/labeled_dice.cs create mode 100644 examples/dotnet/csharp/langford.cs create mode 100644 examples/dotnet/csharp/least_diff.cs create mode 100644 examples/dotnet/csharp/lectures.cs create mode 100644 examples/dotnet/csharp/magic_sequence.cs create mode 100644 examples/dotnet/csharp/magic_square.cs create mode 100644 examples/dotnet/csharp/magic_square_and_cards.cs create mode 100644 examples/dotnet/csharp/map.cs create mode 100644 examples/dotnet/csharp/map2.cs create mode 100644 examples/dotnet/csharp/marathon2.cs create mode 100644 examples/dotnet/csharp/max_flow_taha.cs create mode 100644 examples/dotnet/csharp/max_flow_winston1.cs create mode 100644 examples/dotnet/csharp/minesweeper.cs create mode 100644 examples/dotnet/csharp/mr_smith.cs create mode 100644 examples/dotnet/csharp/nontransitive_dice.cs create mode 100644 examples/dotnet/csharp/nqueens.cs create mode 100644 examples/dotnet/csharp/nurse_rostering_regular.cs create mode 100644 examples/dotnet/csharp/nurse_rostering_transition.cs create mode 100644 examples/dotnet/csharp/nurses_sat.cs create mode 100644 examples/dotnet/csharp/olympic.cs create mode 100644 examples/dotnet/csharp/organize_day.cs create mode 100644 examples/dotnet/csharp/organize_day_intervals.cs create mode 100644 examples/dotnet/csharp/p_median.cs create mode 100644 examples/dotnet/csharp/pandigital_numbers.cs create mode 100644 examples/dotnet/csharp/partition.cs create mode 100644 examples/dotnet/csharp/perfect_square_sequence.cs create mode 100644 examples/dotnet/csharp/photo_problem.cs create mode 100644 examples/dotnet/csharp/place_number_puzzle.cs create mode 100644 examples/dotnet/csharp/post_office_problem2.cs create mode 100644 examples/dotnet/csharp/quasigroup_completion.cs create mode 100644 examples/dotnet/csharp/regex.cs create mode 100644 examples/dotnet/csharp/rogo2.cs create mode 100644 examples/dotnet/csharp/scheduling_speakers.cs create mode 100644 examples/dotnet/csharp/secret_santa.cs create mode 100644 examples/dotnet/csharp/secret_santa2.cs create mode 100644 examples/dotnet/csharp/send_more_money.cs create mode 100644 examples/dotnet/csharp/send_more_money2.cs create mode 100644 examples/dotnet/csharp/send_most_money.cs create mode 100644 examples/dotnet/csharp/seseman.cs create mode 100644 examples/dotnet/csharp/set_covering.cs create mode 100644 examples/dotnet/csharp/set_covering2.cs create mode 100644 examples/dotnet/csharp/set_covering3.cs create mode 100644 examples/dotnet/csharp/set_covering4.cs create mode 100644 examples/dotnet/csharp/set_covering_deployment.cs create mode 100644 examples/dotnet/csharp/set_covering_skiena.cs create mode 100644 examples/dotnet/csharp/set_partition.cs create mode 100644 examples/dotnet/csharp/sicherman_dice.cs create mode 100644 examples/dotnet/csharp/ski_assignment.cs create mode 100644 examples/dotnet/csharp/slow_scheduling.cs create mode 100644 examples/dotnet/csharp/stable_marriage.cs create mode 100644 examples/dotnet/csharp/strimko2.cs create mode 100644 examples/dotnet/csharp/subset_sum.cs create mode 100644 examples/dotnet/csharp/sudoku.cs create mode 100644 examples/dotnet/csharp/survo_puzzle.cs create mode 100644 examples/dotnet/csharp/techtalk_scheduling.cs create mode 100644 examples/dotnet/csharp/to_num.cs create mode 100644 examples/dotnet/csharp/traffic_lights.cs create mode 100644 examples/dotnet/csharp/volsay.cs create mode 100644 examples/dotnet/csharp/volsay2.cs create mode 100644 examples/dotnet/csharp/volsay3.cs create mode 100644 examples/dotnet/csharp/wedding_optimal_chart.cs create mode 100644 examples/dotnet/csharp/who_killed_agatha.cs create mode 100644 examples/dotnet/csharp/word_square.cs create mode 100644 examples/dotnet/csharp/xkcd.cs create mode 100644 examples/dotnet/csharp/young_tableaux.cs create mode 100644 examples/dotnet/csharp/zebra.cs diff --git a/.gitignore b/.gitignore index 98791f74cc..5e3028f598 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,9 @@ ortools/fsharp/**/bin ortools/fsharp/**/obj ortools/fsharp/**/packages +examples/dotnet/**/bin +examples/dotnet/**/obj + ortools/dotnet/**/bin ortools/dotnet/**/obj ortools/dotnet/**/packages diff --git a/examples/dotnet/OrTools.sln b/examples/dotnet/OrTools.sln new file mode 100644 index 0000000000..e97137a88d --- /dev/null +++ b/examples/dotnet/OrTools.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "examples", "csharp\examples.csproj", "{0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|x64.ActiveCfg = Debug|x64 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|x64.Build.0 = Debug|x64 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|x86.ActiveCfg = Debug|x86 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Debug|x86.Build.0 = Debug|x86 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|Any CPU.Build.0 = Release|Any CPU + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|x64.ActiveCfg = Release|x64 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|x64.Build.0 = Release|x64 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|x86.ActiveCfg = Release|x86 + {0899C5EB-2AD1-49C1-9AB3-735E5B81BF56}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/examples/dotnet/README.md b/examples/dotnet/README.md new file mode 100644 index 0000000000..5aabe2a33a --- /dev/null +++ b/examples/dotnet/README.md @@ -0,0 +1,21 @@ +# Dotnet Core examples + +The following examples showcase how to use OrTools. The project solution has examples for both C# and F#. + +We recommend that all projects you create target `netcoreapp2.0` as this allows you to compile for various frameworks and keep up-to-date with the latest frameworks. + +Wherever you have ortools installed, be sure to reference the `Google.OrTools.dll` from the project file. You will also need to reference the library folder housing native libraries. + +### Linux +To reference a particular folder on linux, you can either: explicitly set the **LD_LIBRARY_PATH**; or create a new configuration file with the path of the library folder in `/etc/ld.so.conf.d/` and then run `sudo ldconfig`. The former will set the path on a system level so that you don't have to use the environment. + +### MacOS +To reference a particular folder on linux, you can explicitly set the **DYLD_FALLBACK_LIBRARY_PATH** + +## CSharp + +By default all the examples are compiled in a console applicaiton with the startup object being the **Classname.Main** so that when compiled the entrypoint will be known. + +## FSharp + +TBD \ No newline at end of file diff --git a/examples/dotnet/csharp/3_jugs_regular.cs b/examples/dotnet/csharp/3_jugs_regular.cs new file mode 100644 index 0000000000..e9febc5f34 --- /dev/null +++ b/examples/dotnet/csharp/3_jugs_regular.cs @@ -0,0 +1,293 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ThreeJugsRegular +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + + } + + } + + + + + /** + * + * 3 jugs problem using regular constraint in Google CP Solver. + * + * A.k.a. water jugs problem. + * + * Problem from Taha 'Introduction to Operations Research', + * page 245f . + * + * For more info about the problem, see: + * http://mathworld.wolfram.com/ThreeJugProblem.html + * + * This model use a regular constraint for handling the + * transitions between the states. Instead of minimizing + * the cost in a cost matrix (as shortest path problem), + * we here call the model with increasing length of the + * sequence array (x). + * + * + * Also see http://www.hakank.org/or-tools/3_jugs_regular.py + * + */ + private static bool Solve(int n) + { + Solver solver = new Solver("ThreeJugProblem"); + + // + // Data + // + + // the DFA (for regular) + int n_states = 14; + int input_max = 15; + int initial_state = 1; // state 0 is for the failing state + int[] accepting_states = {15}; + + // + // Manually crafted DFA + // (from the adjacency matrix used in the other models) + // + /* + int[,] transition_fn = { + // 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + {0, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 1 + {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 2 + {0, 0, 0, 4, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 3 + {0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 4 + {0, 0, 0, 0, 0, 6, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 5 + {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, // 6 + {0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0}, // 7 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, // 8 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0}, // 9 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0}, // 10 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0}, // 11 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0}, // 12 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0}, // 13 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, // 14 + // 15 + }; + */ + + // + // However, the DFA is easy to create from adjacency lists. + // + int[][] states = { + new int[] {2,9}, // state 1 + new int[] {3}, // state 2 + new int[] {4, 9}, // state 3 + new int[] {5}, // state 4 + new int[] {6,9}, // state 5 + new int[] {7}, // state 6 + new int[] {8,9}, // state 7 + new int[] {15}, // state 8 + new int[] {10}, // state 9 + new int[] {11}, // state 10 + new int[] {12}, // state 11 + new int[] {13}, // state 12 + new int[] {14}, // state 13 + new int[] {15} // state 14 + }; + + int[,] transition_fn = new int[n_states,input_max]; + for(int i = 0; i < n_states; i++) { + for(int j = 1; j <= input_max; j++) { + bool in_states = false; + for(int s = 0; s < states[i].Length; s++) { + if (j == states[i][s]) { + in_states = true; + break; + } + } + if (in_states) { + transition_fn[i,j-1] = j; + } else { + transition_fn[i,j-1] = 0; + } + } + } + + // + // The name of the nodes, for printing + // the solution. + // + string[] nodes = { + "8,0,0", // 1 start + "5,0,3", // 2 + "5,3,0", // 3 + "2,3,3", // 4 + "2,5,1", // 5 + "7,0,1", // 6 + "7,1,0", // 7 + "4,1,3", // 8 + "3,5,0", // 9 + "3,2,3", // 10 + "6,2,0", // 11 + "6,0,2", // 12 + "1,5,2", // 13 + "1,4,3", // 14 + "4,4,0" // 15 goal + }; + + // + // Decision variables + // + + // Note: We use 1..2 (instead of 0..1) and subtract 1 in the solution + IntVar[] x = solver.MakeIntVarArray(n, 1, input_max, "x"); + + + // + // Constraints + // + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + bool found = false; + while (solver.NextSolution()) { + Console.WriteLine("\nFound a solution of length {0}", n+1); + int[] x_val = new int[n]; + x_val[0] = 1; + Console.WriteLine("{0} -> {1}", nodes[0], nodes[x_val[0]]); + for(int i = 1; i < n; i++) { + // Note: here we subtract 1 to get 0..1 + int val = (int)x[i].Value()-1; + x_val[i] = val; + Console.WriteLine("{0} -> {1}", nodes[x_val[i-1]], nodes[x_val[i]]); + } + Console.WriteLine(); + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + found = true; + + } + + solver.EndSearch(); + + return found; + + } + + public static void Main(String[] args) + { + + for(int n = 1; n < 15; n++) { + bool found = Solve(n); + if (found) { + break; + } + } + } +} diff --git a/examples/dotnet/csharp/TaskScheduling.cs b/examples/dotnet/csharp/TaskScheduling.cs new file mode 100644 index 0000000000..b2e5b09304 --- /dev/null +++ b/examples/dotnet/csharp/TaskScheduling.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +namespace OrToolsConstraint { +class Job { + public Job(List tasks) { + AlternativeTasks = tasks; + } + public Job Successor { get; set; } + public List AlternativeTasks { get; set; } +} + +class Task { + public Task(string name, long duration, long equipment) { + Name = name; + Duration = duration; + Equipment = equipment; + } + + public string Name {get; set;} + public long StartTime {get; set;} + public long EndTime { + get { + return StartTime + Duration; + } + } + public long Duration {get; set;} + public long Equipment { get; set; } + + public override string ToString() { + return Name + " [ " + Equipment + " ]\tstarts: " + StartTime + " ends:" + + EndTime + ", duration: " + Duration; + } +} + +class Prefix : VoidToString +{ + public override string Run() + { + return "[TaskScheduling] "; + } +} + +class TaskScheduling { + public static List myJobList = new List(); + public static Dictionary> tasksToEquipment = + new Dictionary>(); + public static Dictionary taskIndexes = + new Dictionary(); + + public static void InitTaskList() { + List taskList = new List(); + taskList.Add(new Task("Job1Task0a", 15, 0)); + taskList.Add(new Task("Job1Task0b", 25, 1)); + taskList.Add(new Task("Job1Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task1a", 25, 0)); + taskList.Add(new Task("Job1Task1b", 30, 1)); + taskList.Add(new Task("Job1Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task2a", 20, 0)); + taskList.Add(new Task("Job1Task2b", 35, 1)); + taskList.Add(new Task("Job1Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task0a", 15, 0)); + taskList.Add(new Task("Job2Task0b", 25, 1)); + taskList.Add(new Task("Job2Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task1a", 25, 0)); + taskList.Add(new Task("Job2Task1b", 30, 1)); + taskList.Add(new Task("Job2Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task2a", 20, 0)); + taskList.Add(new Task("Job2Task2b", 35, 1)); + taskList.Add(new Task("Job2Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task0a", 10, 0)); + taskList.Add(new Task("Job3Task0b", 15, 1)); + taskList.Add(new Task("Job3Task0c", 50, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task1a", 50, 0)); + taskList.Add(new Task("Job3Task1b", 10, 1)); + taskList.Add(new Task("Job3Task1c", 20, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task2a", 65, 0)); + taskList.Add(new Task("Job3Task2b", 5, 1)); + taskList.Add(new Task("Job3Task2c", 15, 2)); + myJobList.Add(new Job(taskList)); + + myJobList[0].Successor = myJobList[1]; + myJobList[1].Successor = myJobList[2]; + myJobList[2].Successor = null; + + myJobList[3].Successor = myJobList[4]; + myJobList[4].Successor = myJobList[5]; + myJobList[5].Successor = null; + + myJobList[6].Successor = myJobList[7]; + myJobList[7].Successor = myJobList[8]; + myJobList[8].Successor = null; + } + + public static int GetTaskCount() { + int c = 0; + foreach (Job j in myJobList) + foreach (Task t in j.AlternativeTasks) { + taskIndexes[t.Name] = c; + c++; + } + + return c; + } + + public static int GetEndTaskCount() { + int c = 0; + foreach (Job j in myJobList) + if (j.Successor == null) + c += j.AlternativeTasks.Count; + return c; + } + + static void Main(string[] args) { + InitTaskList(); + int taskCount = GetTaskCount(); + + Solver solver = new Solver("ResourceConstraintScheduling"); + + IntervalVar[] tasks = new IntervalVar[taskCount]; + IntVar[] taskChoosed = new IntVar[taskCount]; + IntVar[] makeSpan = new IntVar[GetEndTaskCount()]; + + int endJobCounter = 0; + foreach (Job j in myJobList) { + IntVar[] tmp = new IntVar[j.AlternativeTasks.Count]; + int i = 0; + foreach (Task t in j.AlternativeTasks) { + long ti = taskIndexes[t.Name]; + taskChoosed[ti] = solver.MakeIntVar(0, 1, t.Name + "_choose"); + tmp[i++] = taskChoosed[ti]; + tasks[ti] = solver.MakeFixedDurationIntervalVar( + 0, 100000, t.Duration, false, t.Name + "_interval"); + if (j.Successor == null) + makeSpan[endJobCounter++] = tasks[ti].EndExpr().Var(); + if (!tasksToEquipment.ContainsKey(t.Equipment)) + tasksToEquipment[t.Equipment] = new List(); + tasksToEquipment[t.Equipment].Add(tasks[ti]); + } + solver.Add(IntVarArrayHelper.Sum(tmp) == 1); + } + + List all_seq = new List(); + foreach (KeyValuePair> pair in tasksToEquipment) { + DisjunctiveConstraint dc = solver.MakeDisjunctiveConstraint( + pair.Value.ToArray(), pair.Key.ToString()); + solver.Add(dc); + all_seq.Add(dc.SequenceVar()); + } + + IntVar objective_var = solver.MakeMax(makeSpan).Var(); + OptimizeVar objective_monitor = solver.MakeMinimize(objective_var, 1); + + DecisionBuilder sequence_phase = + solver.MakePhase(all_seq.ToArray(), Solver.SEQUENCE_DEFAULT); + DecisionBuilder objective_phase = + solver.MakePhase(objective_var, Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + DecisionBuilder main_phase = solver.Compose(sequence_phase, objective_phase); + + const int kLogFrequency = 1000000; + VoidToString prefix = new Prefix(); + SearchMonitor search_log = + solver.MakeSearchLog(kLogFrequency, objective_monitor, prefix); + + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.Add(all_seq.ToArray()); + collector.AddObjective(objective_var); + + if (solver.Solve(main_phase, search_log, objective_monitor, null, collector)) + Console.Out.WriteLine("Optimal solution = " + collector.ObjectiveValue(0)); + else + Console.Out.WriteLine("No solution."); + } +} +} diff --git a/examples/dotnet/csharp/a_puzzle.cs b/examples/dotnet/csharp/a_puzzle.cs new file mode 100644 index 0000000000..ccf9397505 --- /dev/null +++ b/examples/dotnet/csharp/a_puzzle.cs @@ -0,0 +1,263 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class APuzzle +{ + /** + * + * From "God plays dice" + * "A puzzle" + * http://gottwurfelt.wordpress.com/2012/02/22/a-puzzle/ + * And the sequel "Answer to a puzzle" + * http://gottwurfelt.wordpress.com/2012/02/24/an-answer-to-a-puzzle/ + * + * This problem instance was taken from the latter blog post. + * (Problem 1) + * + * """ + * 8809 = 6 + * 7111 = 0 + * 2172 = 0 + * 6666 = 4 + * 1111 = 0 + * 3213 = 0 + * 7662 = 2 + * 9312 = 1 + * 0000 = 4 + * 2222 = 0 + * 3333 = 0 + * 5555 = 0 + * 8193 = 3 + * 8096 = 5 + * 7777 = 0 + * 9999 = 4 + * 7756 = 1 + * 6855 = 3 + * 9881 = 5 + * 5531 = 0 + * + * 2581 = ? + * """ + * + * Note: + * This model yields 10 solutions, since x4 is not + * restricted in the constraints. + * All solutions has x assigned to the correct result. + * + * + * (Problem 2) + * The problem stated in "A puzzle" + * http://gottwurfelt.wordpress.com/2012/02/22/a-puzzle/ + * is + * """ + * 8809 = 6 + * 7662 = 2 + * 9312 = 1 + * 8193 = 3 + * 8096 = 5 + * 7756 = 1 + * 6855 = 3 + * 9881 = 5 + * + * 2581 = ? + * """ + * This problem instance yields two different solutions of x, + * one is the same (correct) as for the above problem instance, + * and one is not. + * This is because here x0,x1,x4 and x9 are underdefined. + * + * + */ + private static void Solve(int p = 1) + { + Solver solver = new Solver("APuzzle"); + + Console.WriteLine("\nSolving p{0}", p); + + + // + // Data + // + int n = 10; + + // + // Decision variables + // + IntVar x0 = solver.MakeIntVar(0, n-1, "x0"); + IntVar x1 = solver.MakeIntVar(0, n-1, "x1"); + IntVar x2 = solver.MakeIntVar(0, n-1, "x2"); + IntVar x3 = solver.MakeIntVar(0, n-1, "x3"); + IntVar x4 = solver.MakeIntVar(0, n-1, "x4"); + IntVar x5 = solver.MakeIntVar(0, n-1, "x5"); + IntVar x6 = solver.MakeIntVar(0, n-1, "x6"); + IntVar x7 = solver.MakeIntVar(0, n-1, "x7"); + IntVar x8 = solver.MakeIntVar(0, n-1, "x8"); + IntVar x9 = solver.MakeIntVar(0, n-1, "x9"); + + IntVar[] all = {x0,x1,x2,x3,x4,x5,x6,x7,x8,x9}; + + // The unknown, i.e. 2581 = x + IntVar x = solver.MakeIntVar(0, n-1, "x"); + + + // + // Constraints + // + + // Both problem are here shown in two + // approaches: + // - using equations + // - using a a matrix and Sum of each row + + if (p == 1) { + + // Problem 1 + solver.Add(x8+x8+x0+x9 == 6); + solver.Add(x7+x1+x1+x1 == 0); + solver.Add(x2+x1+x7+x2 == 0); + solver.Add(x6+x6+x6+x6 == 4); + solver.Add(x1+x1+x1+x1 == 0); + solver.Add(x3+x2+x1+x3 == 0); + solver.Add(x7+x6+x6+x2 == 2); + solver.Add(x9+x3+x1+x2 == 1); + solver.Add(x0+x0+x0+x0 == 4); + solver.Add(x2+x2+x2+x2 == 0); + solver.Add(x3+x3+x3+x3 == 0); + solver.Add(x5+x5+x5+x5 == 0); + solver.Add(x8+x1+x9+x3 == 3); + solver.Add(x8+x0+x9+x6 == 5); + solver.Add(x7+x7+x7+x7 == 0); + solver.Add(x9+x9+x9+x9 == 4); + solver.Add(x7+x7+x5+x6 == 1); + solver.Add(x6+x8+x5+x5 == 3); + solver.Add(x9+x8+x8+x1 == 5); + solver.Add(x5+x5+x3+x1 == 0); + + // The unknown + solver.Add(x2+x5+x8+x1 == x); + + } else if (p == 2) { + + // Another representation of Problem 1 + int[,] problem1 = { + {8,8,0,9, 6}, + {7,1,1,1, 0}, + {2,1,7,2, 0}, + {6,6,6,6, 4}, + {1,1,1,1, 0}, + {3,2,1,3, 0}, + {7,6,6,2, 2}, + {9,3,1,2, 1}, + {0,0,0,0, 4}, + {2,2,2,2, 0}, + {3,3,3,3, 0}, + {5,5,5,5, 0}, + {8,1,9,3, 3}, + {8,0,9,6, 5}, + {7,7,7,7, 0}, + {9,9,9,9, 4}, + {7,7,5,6, 1}, + {6,8,5,5, 3}, + {9,8,8,1, 5}, + {5,5,3,1, 0} + }; + + for(int i = 0; i < problem1.GetLength(0); i++) { + solver.Add( (from j in Enumerable.Range(0, 4) + select all[problem1[i,j]] + ).ToArray().Sum() == problem1[i,4] ); + } + + solver.Add(all[2]+all[5]+all[8]+all[1] == x); + + } else if (p == 3) { + + // Problem 2 + solver.Add(x8+x8+x0+x9 == 6); + solver.Add(x7+x6+x6+x2 == 2); + solver.Add(x9+x3+x1+x2 == 1); + solver.Add(x8+x1+x9+x3 == 3); + solver.Add(x8+x0+x9+x6 == 5); + solver.Add(x7+x7+x5+x6 == 1); + solver.Add(x6+x8+x5+x5 == 3); + solver.Add(x9+x8+x8+x1 == 5); + + // The unknown + solver.Add(x2+x5+x8+x1 == x); + + } else { + + // Another representation of Problem 2 + int[,] problem2 = { + {8,8,0,9, 6}, + {7,6,6,2, 2}, + {9,3,1,2, 1}, + {8,1,9,3, 3}, + {8,0,9,6, 5}, + {7,7,5,6, 1}, + {6,8,5,5, 3}, + {9,8,8,1, 5} + }; + + for(int i = 0; i < problem2.GetLength(0); i++) { + solver.Add( (from j in Enumerable.Range(0, 4) + select all[problem2[i,j]] + ).ToArray().Sum() == problem2[i,4] ); + } + + + solver.Add(all[2]+all[5]+all[8]+all[1] == x); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + int c = 0; + while (solver.NextSolution()) { + Console.Write("x: {0} x0..x9: ", x.Value()); + for(int i = 0; i < n; i++) { + Console.Write(all[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + for(int p = 1; p <= 4; p++) { + Solve(p); + } + } +} diff --git a/examples/dotnet/csharp/a_round_of_golf.cs b/examples/dotnet/csharp/a_round_of_golf.cs new file mode 100644 index 0000000000..fb1316d125 --- /dev/null +++ b/examples/dotnet/csharp/a_round_of_golf.cs @@ -0,0 +1,183 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class ARoundOfGolf +{ + + /** + * + * A Round of Golf puzzle (Dell Logic Puzzles) in Google CP Solver. + * + * From http://brownbuffalo.sourceforge.net/RoundOfGolfClues.html + * """ + * Title: A Round of Golf + * Author: Ellen K. Rodehorst + * Publication: Dell Favorite Logic Problems + * Issue: Summer, 2000 + * Puzzle #: 9 + * Stars: 1 + * + * When the Sunny Hills Country Club golf course isn't in use by club members, + * of course, it's open to the club's employees. Recently, Jack and three other + * workers at the golf course got together on their day off to play a round of + * eighteen holes of golf. + * Afterward, all four, including Mr. Green, went to the clubhouse to total + * their scorecards. Each man works at a different job (one is a short-order + * cook), and each shot a different score in the game. No one scored below + * 70 or above 85 strokes. From the clues below, can you discover each man's + * full name, job and golf score? + * + * 1. Bill, who is not the maintenance man, plays golf often and had the lowest + * score of the foursome. + * 2. Mr. Clubb, who isn't Paul, hit several balls into the woods and scored ten + * strokes more than the pro-shop clerk. + * 3. In some order, Frank and the caddy scored four and seven more strokes than + * Mr. Sands. + * 4. Mr. Carter thought his score of 78 was one of his better games, even + * though Frank's score was lower. + * 5. None of the four scored exactly 81 strokes. + * + * Determine: First Name - Last Name - Job - Score + * """ + * + * See http://www.hakank.org/google_or_tools/a_round_of_golf.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ARoundOfGolf"); + + // number of speakers + int n = 4; + + int Jack = 0; + int Bill = 1; + int Paul = 2; + int Frank = 3; + + // + // Decision variables + // + IntVar[] last_name = solver.MakeIntVarArray(n, 0, n-1, "last_name"); + // IntVar Green = last_name[0]; // not used + IntVar Clubb = last_name[1]; + IntVar Sands = last_name[2]; + IntVar Carter = last_name[3]; + + IntVar[] job = solver.MakeIntVarArray(n, 0, n-1, "job"); + // IntVar cook = job[0]; // not used + IntVar maintenance_man = job[1]; + IntVar clerk = job[2]; + IntVar caddy = job[3]; + + IntVar[] score = solver.MakeIntVarArray(n, 70, 85, "score"); + + // for search + IntVar[] all = new IntVar[n*3]; + for(int i = 0; i < n; i++) { + all[i] = last_name[i]; + all[i+n] = job[i]; + all[i+2*n] = score[i]; + } + + // + // Constraints + // + solver.Add(last_name.AllDifferent()); + solver.Add(job.AllDifferent()); + solver.Add(score.AllDifferent()); + + // 1. Bill, who is not the maintenance man, plays golf often and had + // the lowest score of the foursome. + solver.Add(maintenance_man != Bill); + solver.Add(score[Bill] < score[Jack]); + solver.Add(score[Bill] < score[Paul]); + solver.Add(score[Bill] < score[Frank]); + + // 2. Mr. Clubb, who isn't Paul, hit several balls into the woods and + // scored ten strokes more than the pro-shop clerk. + solver.Add(Clubb != Paul); + solver.Add(score.Element(Clubb) == score.Element(clerk) + 10); + + // 3. In some order, Frank and the caddy scored four and seven more + // strokes than Mr. Sands. + solver.Add(caddy != Frank); + solver.Add(Sands != Frank); + solver.Add(caddy != Sands); + + IntVar b3_a_1 = score.Element(Sands) + 4 == score[Frank]; + IntVar b3_a_2 = score.Element(caddy) == score.Element(Sands) + 7; + IntVar b3_b_1 = score.Element(Sands) + 7 == score[Frank]; + IntVar b3_b_2 = score.Element(caddy) == score.Element(Sands) + 4; + solver.Add( (b3_a_1*b3_a_2) + (b3_b_1*b3_b_2) == 1); + + // 4. Mr. Carter thought his score of 78 was one of his better games, + // even though Frank's score was lower. + solver.Add(Carter != Frank); + solver.Add(score.Element(Carter) == 78); + solver.Add(score[Frank] < score.Element(Carter)); + + // 5. None of the four scored exactly 81 strokes. + for(int i = 0; i < n; i++) { + solver.Add(score[i] != 81); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine( + "Last name: " + + String.Join(", ", (from i in last_name + select i.Value().ToString()).ToArray())); + Console.WriteLine( + "Job : " + + String.Join(", ", (from i in job + select i.Value().ToString()).ToArray())); + Console.WriteLine( + "Score : " + + String.Join(", ", (from i in score + select i.Value().ToString()).ToArray())); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/all_interval.cs b/examples/dotnet/csharp/all_interval.cs new file mode 100644 index 0000000000..793db46ac0 --- /dev/null +++ b/examples/dotnet/csharp/all_interval.cs @@ -0,0 +1,100 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class AllInterval +{ + + /** + * + * Implements the all interval problem. + * See http://www.hakank.org/google_or_tools/all_interval.py + * + */ + private static void Solve(int n=12) + { + Solver solver = new Solver("AllInterval"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + IntVar[] diffs = solver.MakeIntVarArray(n-1, 1, n-1, "diffs"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(diffs.AllDifferent()); + + for(int k = 0; k < n - 1; k++) { + // solver.Add(diffs[k] == (x[k + 1] - x[k]).Abs()); + solver.Add(diffs[k] == (x[k + 1] - x[k].Abs())); + } + + + // symmetry breaking + solver.Add(x[0] < x[n - 1]); + solver.Add(diffs[0] < diffs[1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.Write(" diffs: "); + for(int i = 0; i < n-1; i++) { + Console.Write("{0} ", diffs[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 12; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/alldifferent_except_0.cs b/examples/dotnet/csharp/alldifferent_except_0.cs new file mode 100644 index 0000000000..cbe0c6a13e --- /dev/null +++ b/examples/dotnet/csharp/alldifferent_except_0.cs @@ -0,0 +1,103 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + + +public class AllDifferentExcept0Test +{ + + // + // Decomposition of alldifferent_except_0 + // + public static void AllDifferentExcept0(Solver solver, IntVar[] a) { + + int n = a.Length; + for(int i = 0; i < n; i++) { + for(int j = 0; j < i; j++) { + solver.Add((a[i] != 0) * (a[j] != 0) <= (a[i] != a[j])); + } + } + } + + + /** + * + * Decomposition of alldifferent_except_0 + * + * See http://www.hakank.org/google_or_tools/map.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("AllDifferentExcept0"); + + // + // data + // + int n = 6; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n - 1 , "x"); + + // + // Constraints + // + AllDifferentExcept0(solver, x); + + // we also require at least 2 0's + IntVar[] z_tmp = new IntVar[n]; + for(int i = 0; i < n; i++) { + z_tmp[i] = x[i] == 0; + } + IntVar z = z_tmp.Sum().VarWithName("z"); + solver.Add(z == 2); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("z: {0} x: ", z.Value()); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/assignment.cs b/examples/dotnet/csharp/assignment.cs new file mode 100644 index 0000000000..17d9e0821d --- /dev/null +++ b/examples/dotnet/csharp/assignment.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Assignment +{ + + /** + * + * Assignment problem + * + * From Wayne Winston "Operations Research", + * Assignment Problems, page 393f + * (generalized version with added test column) + * + * See See http://www.hakank.org/or-tools/assignment.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Assignment"); + + // + // data + // + + // Problem instance + // hakank: I added the fifth column to make it more + // interesting + int rows = 4; + int cols = 5; + int[,] cost = { + {14, 5, 8, 7, 15}, + { 2, 12, 6, 5, 3}, + { 7, 8, 3, 9, 7}, + { 2, 4, 6, 10, 1} + }; + + + // + // Decision variables + // + IntVar[,] x = solver.MakeBoolVarMatrix(rows, cols, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // Exacly one assignment per row (task), + // i.e. all rows must be assigned with one worker + for(int i = 0; i < rows; i++) { + solver.Add((from j in Enumerable.Range(0, cols) + select x[i,j]).ToArray().Sum() == 1); + } + + // At most one assignments per column (worker) + for(int j = 0; j < cols; j++) { + solver.Add((from i in Enumerable.Range(0, rows) + select x[i,j]).ToArray().Sum() <= 1); + } + + // Total cost + IntVar total_cost = (from i in Enumerable.Range(0, rows) + from j in Enumerable.Range(0, cols) + select (cost[i,j] * x[i,j])).ToArray().Sum().Var(); + + // + // objective + // + OptimizeVar objective = total_cost.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("total_cost: {0}", total_cost.Value()); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + Console.WriteLine("Assignments:"); + for(int i = 0; i < rows; i++) { + Console.Write("Task " + i); + for(int j = 0; j < cols; j++) { + if (x[i,j].Value() == 1) { + Console.WriteLine(" is done by " + j); + } + } + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/broken_weights.cs b/examples/dotnet/csharp/broken_weights.cs new file mode 100644 index 0000000000..ff05dbb9a6 --- /dev/null +++ b/examples/dotnet/csharp/broken_weights.cs @@ -0,0 +1,167 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class BrokenWeights +{ + + /** + * + * Broken weights problem. + * + * From http://www.mathlesstraveled.com/?p=701 + * """ + * Here's a fantastic problem I recently heard. Apparently it was first + * posed by Claude Gaspard Bachet de Meziriac in a book of arithmetic problems + * published in 1612, and can also be found in Heinrich Dorrie's 100 + * Great Problems of Elementary Mathematics. + * + * A merchant had a forty pound measuring weight that broke + * into four pieces as the result of a fall. When the pieces were + * subsequently weighed, it was found that the weight of each piece + * was a whole number of pounds and that the four pieces could be + * used to weigh every integral weight between 1 and 40 pounds. What + * were the weights of the pieces? + * + * Note that since this was a 17th-century merchant, he of course used a + * balance scale to weigh things. So, for example, he could use a 1-pound + * weight and a 4-pound weight to weigh a 3-pound object, by placing the + * 3-pound object and 1-pound weight on one side of the scale, and + * the 4-pound weight on the other side. + * """ + * + * Also see http://www.hakank.org/or-tools/broken_weights.py + * + */ + private static void Solve(int m=40, int n=4) + { + Solver solver = new Solver("BrokenWeights"); + + Console.WriteLine("Total weight (m): {0}", m); + Console.WriteLine("Number of pieces (n): {0}", n); + Console.WriteLine(); + + + // + // Decision variables + // + + IntVar[] weights = solver.MakeIntVarArray(n, 1, m , "weights"); + IntVar[,] x = new IntVar[m, n]; + // Note: in x_flat we insert the weights array before x + IntVar[] x_flat = new IntVar[m*n + n]; + for(int j = 0; j < n; j++) { + x_flat[j] = weights[j]; + } + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + x[i,j] = solver.MakeIntVar(-1, 1, "x["+i+","+j+"]"); + x_flat[n+i*n+j] = x[i,j]; + } + } + + + + // + // Constraints + // + + + // symmetry breaking + for(int j = 1; j < n; j++) { + solver.Add(weights[j-1] < weights[j]); + } + + + solver.Add(weights.Sum() == m); + + // Check that all weights from 1 to n (default 40) can be made. + // + // Since all weights can be on either side + // of the side of the scale we allow either + // -1, 0, or 1 of the weights, assuming that + // -1 is the weights on the left and 1 is on the right. + // + for(int i = 0; i < m; i++) { + solver.Add( (from j in Enumerable.Range(0, n) + select weights[j] * x[i,j]).ToArray().Sum() == i+1); + } + + + // + // The objective is to minimize the last weight. + // + OptimizeVar obj = weights[n-1].Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("weights: "); + for(int i = 0; i < n; i++) { + Console.Write("{0,3} ", weights[i].Value()); + } + Console.WriteLine(); + for(int i = 0; i < 10+n*4; i++) { + Console.Write("-"); + } + Console.WriteLine(); + for(int i = 0; i < m; i++) { + Console.Write("weight {0,2}:", i+1); + for(int j = 0; j < n; j++) { + Console.Write("{0,3} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int m = 40; + int n = 4; + + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + Solve(m, n); + } +} diff --git a/examples/dotnet/csharp/bus_schedule.cs b/examples/dotnet/csharp/bus_schedule.cs new file mode 100644 index 0000000000..5b23e569e3 --- /dev/null +++ b/examples/dotnet/csharp/bus_schedule.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class BusSchedule +{ + + + /** + * + * Bus scheduling. + * + * Minimize number of buses in timeslots. + * + * Problem from Taha "Introduction to Operations Research", page 58. + * + * This is a slightly more general model than Taha's. + * + * Also see, http://www.hakank.org/or-tools/bus_schedule.py + * + */ + private static long Solve(long num_buses_check = 0) + { + + Solver solver = new Solver("BusSchedule"); + + // + // data + // + int time_slots = 6; + int[] demands = {8, 10, 7, 12, 4, 4}; + int max_num = demands.Sum(); + + // + // Decision variables + // + + // How many buses start the schedule at time slot t + IntVar[] x = solver.MakeIntVarArray(time_slots, 0, max_num, "x"); + // Total number of buses + IntVar num_buses = x.Sum().VarWithName("num_buses"); + + // + // Constraints + // + + // Meet the demands for this and the next time slot. + for(int i = 0; i < time_slots - 1; i++) { + solver.Add(x[i]+x[i+1] >= demands[i]); + } + + // The demand "around the clock" + solver.Add(x[time_slots-1] + x[0] - demands[time_slots-1] == 0); + + // For showing all solutions of minimal number of buses + if (num_buses_check > 0) { + solver.Add(num_buses == num_buses_check); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (num_buses_check == 0) { + + // Minimize num_buses + OptimizeVar obj = num_buses.Minimize(1); + solver.NewSearch(db, obj); + + } else { + + solver.NewSearch(db); + + } + + long result = 0; + while (solver.NextSolution()) { + result = num_buses.Value(); + Console.Write("x: "); + for(int i = 0; i < time_slots; i++) { + Console.Write("{0,2} ", x[i].Value()); + } + Console.WriteLine("num_buses: " + num_buses.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + return result; + + } + + + + public static void Main(String[] args) + { + + Console.WriteLine("Check for minimum number of buses: "); + long num_buses = Solve(); + Console.WriteLine("\n... got {0} as minimal value.", num_buses); + Console.WriteLine("\nAll solutions: ", num_buses); + num_buses = Solve(num_buses); + + } +} diff --git a/examples/dotnet/csharp/circuit.cs b/examples/dotnet/csharp/circuit.cs new file mode 100644 index 0000000000..c4026c2159 --- /dev/null +++ b/examples/dotnet/csharp/circuit.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class CircuitTest +{ + + + /** + * circuit(solver, x) + * + * A decomposition of the global constraint circuit, based + * on some observation of the orbits in an array. + * + * Note: The domain of x must be 0..n-1 (not 1..n) + * since C# is 0-based. + */ + public static void circuit(Solver solver, IntVar[] x) { + + int n = x.Length; + IntVar[] z = solver.MakeIntVarArray(n, 0, n - 1, "z"); + + solver.Add(x.AllDifferent()); + solver.Add(z.AllDifferent()); + + // put the orbit of x[0] in z[0..n-1] + solver.Add(z[0] == x[0]); + for(int i = 1; i < n-1; i++) { + solver.Add(z[i] == x.Element(z[i-1])); + } + + // z may not be 0 for i < n-1 + for(int i = 1; i < n - 1; i++) { + solver.Add(z[i] != 0); + } + + // when i = n-1 it must be 0 + solver.Add(z[n - 1] == 0); + + } + + /** + * + * Implements a (decomposition) of the global constraint circuit. + * See http://www.hakank.org/google_or_tools/circuit.py + * + */ + private static void Solve(int n = 5) + { + Solver solver = new Solver("Circuit"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + + // + // Constraints + // + circuit(solver, x); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/circuit2.cs b/examples/dotnet/csharp/circuit2.cs new file mode 100644 index 0000000000..4e2ed0a26b --- /dev/null +++ b/examples/dotnet/csharp/circuit2.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class CircuitTest2 +{ + + + /** + * circuit(solver, x, z) + * + * A decomposition of the global constraint circuit, based + * on some observation of the orbits in an array. + * + * This version also exposes z (the path) to the public. + * + * Note: The domain of x must be 0..n-1 (not 1..n) + * since C# is 0-based. + */ + public static void circuit(Solver solver, IntVar[] x, IntVar[] z) { + + int n = x.Length; + + solver.Add(x.AllDifferent()); + solver.Add(z.AllDifferent()); + + // put the orbit of x[0] in z[0..n-1] + solver.Add(z[0] == x[0]); + for(int i = 1; i < n-1; i++) { + solver.Add(z[i] == x.Element(z[i-1])); + } + + // z may not be 0 for i < n-1 + for(int i = 1; i < n - 1; i++) { + solver.Add(z[i] != 0); + } + + // when i = n-1 it must be 0 + solver.Add(z[n - 1] == 0); + + } + + /** + * + * Implements a (decomposition) of the global constraint circuit + * and extracting the path. + * + * One circuit for n = 5 is 3 0 4 2 1 + * Thus the extracted path is 0 -> 3 -> 2 -> 4 -> 1 -> 0 + * + */ + private static void Solve(int n = 5) + { + Solver solver = new Solver("CircuitTest2"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + IntVar[] path = solver.MakeIntVarArray(n, 0, n-1, "path"); + + // + // Constraints + // + circuit(solver, x, path); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x : "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.Write("\npath: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", path[i].Value()); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/code_samples_sat.cs b/examples/dotnet/csharp/code_samples_sat.cs new file mode 100644 index 0000000000..a9f4be6838 --- /dev/null +++ b/examples/dotnet/csharp/code_samples_sat.cs @@ -0,0 +1,404 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.Sat; + +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} + +public class VarArraySolutionPrinterWithObjective : CpSolverSolutionCallback +{ + public VarArraySolutionPrinterWithObjective(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + Console.WriteLine( + String.Format(" objective value = {0}", ObjectiveValue())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} + + +public class CodeSamplesSat +{ + static void CodeSample() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the Boolean variable. + IntVar x = model.NewBoolVar("x"); + } + + static void LiteralSample() + { + CpModel model = new CpModel(); + IntVar x = model.NewBoolVar("x"); + ILiteral not_x = x.Not(); + } + + static void BoolOrSample() + { + CpModel model = new CpModel(); + IntVar x = model.NewBoolVar("x"); + IntVar y = model.NewBoolVar("y"); + model.AddBoolOr(new ILiteral[] {x, y.Not()}); + } + + static void ReifiedSample() + { + CpModel model = new CpModel(); + + IntVar x = model.NewBoolVar("x"); + IntVar y = model.NewBoolVar("y"); + IntVar b = model.NewBoolVar("b"); + + // First version using a half-reified bool and. + model.AddBoolAnd(new ILiteral[] {x, y.Not()}).OnlyEnforceIf(b); + + // Second version using implications. + model.AddImplication(b, x); + model.AddImplication(b, y.Not()); + + // Third version using bool or. + model.AddBoolOr(new ILiteral[] {b.Not(), x}); + model.AddBoolOr(new ILiteral[] {b.Not(), y.Not()}); + } + + static void RabbitsAndPheasants() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + IntVar r = model.NewIntVar(0, 100, "r"); + IntVar p = model.NewIntVar(0, 100, "p"); + // 20 heads. + model.Add(r + p == 20); + // 56 legs. + model.Add(4 * r + 2 * p == 56); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine(solver.Value(r) + " rabbits, and " + + solver.Value(p) + " pheasants"); + } + } + + static void BinpackingProblem() + { + // Data. + int bin_capacity = 100; + int slack_capacity = 20; + int num_bins = 10; + + int[,] items = new int[,] { {20, 12}, {15, 12}, {30, 8}, {45, 5} }; + int num_items = items.GetLength(0); + + // Model. + CpModel model = new CpModel(); + + // Main variables. + IntVar[,] x = new IntVar[num_items, num_bins]; + for (int i = 0; i < num_items; ++i) + { + int num_copies = items[i, 1]; + for (int b = 0; b < num_bins; ++b) + { + x[i, b] = model.NewIntVar(0, num_copies, String.Format("x_{0}_{1}", i, b)); + } + } + + // Load variables. + IntVar[] load = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + load[b] = model.NewIntVar(0, bin_capacity, String.Format("load_{0}", b)); + } + + // Slack variables. + IntVar[] slacks = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + slacks[b] = model.NewBoolVar(String.Format("slack_{0}", b)); + } + + // Links load and x. + int[] sizes = new int[num_items]; + for (int i = 0; i < num_items; ++i) { + sizes[i] = items[i, 0]; + } + for (int b = 0; b < num_bins; ++b) + { + IntVar[] tmp = new IntVar[num_items]; + for (int i = 0; i < num_items; ++i) + { + tmp[i] = x[i, b]; + } + model.Add(load[b] == tmp.ScalProd(sizes)); + } + + // Place all items. + for (int i = 0; i < num_items; ++i) + { + IntVar[] tmp = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + tmp[b] = x[i, b]; + } + model.Add(tmp.Sum() == items[i, 1]); + } + + // Links load and slack. + int safe_capacity = bin_capacity - slack_capacity; + for (int b = 0; b < num_bins; ++b) + { + // slack[b] => load[b] <= safe_capacity. + model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b]); + // not(slack[b]) => load[b] > safe_capacity. + model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not()); + } + + // Maximize sum of slacks. + model.Maximize(slacks.Sum()); + + // Solves and prints out the solution. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Console.WriteLine(String.Format("Solve status: {0}", status)); + if (status == CpSolverStatus.Optimal) { + Console.WriteLine(String.Format("Optimal objective value: {0}", + solver.ObjectiveValue)); + for (int b = 0; b < num_bins; ++b) + { + Console.WriteLine(String.Format("load_{0} = {1}", + b, solver.Value(load[b]))); + for (int i = 0; i < num_items; ++i) + { + Console.WriteLine(string.Format(" item_{0}_{1} = {2}", + i, b, solver.Value(x[i, b]))); + } + } + } + Console.WriteLine("Statistics"); + Console.WriteLine( + String.Format(" - conflicts : {0}", solver.NumConflicts())); + Console.WriteLine( + String.Format(" - branches : {0}", solver.NumBranches())); + Console.WriteLine( + String.Format(" - wall time : {0} s", solver.WallTime())); + } + + static void IntervalSample() + { + CpModel model = new CpModel(); + int horizon = 100; + IntVar start_var = model.NewIntVar(0, horizon, "start"); + // C# code supports IntVar or integer constants in intervals. + int duration = 10; + IntVar end_var = model.NewIntVar(0, horizon, "end"); + IntervalVar interval = + model.NewIntervalVar(start_var, duration, end_var, "interval"); + } + + static void OptionalIntervalSample() + { + CpModel model = new CpModel(); + int horizon = 100; + IntVar start_var = model.NewIntVar(0, horizon, "start"); + // C# code supports IntVar or integer constants in intervals. + int duration = 10; + IntVar end_var = model.NewIntVar(0, horizon, "end"); + IntVar presence_var = model.NewBoolVar("presence"); + IntervalVar interval = model.NewOptionalIntervalVar( + start_var, duration, end_var, presence_var, "interval"); + } + + static void MinimalCpSat() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Creates the constraints. + model.Add(x != y); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine("x = " + solver.Value(x)); + Console.WriteLine("y = " + solver.Value(y)); + Console.WriteLine("z = " + solver.Value(z)); + } + } + + static void MinimalCpSatWithTimeLimit() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Creates the constraints. + model.Add(x != y); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + + // Adds a time limit. Parameters are stored as strings in the solver. + solver.StringParameters = "max_time_in_seconds:10.0" ; + + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine("x = " + solver.Value(x)); + Console.WriteLine("y = " + solver.Value(y)); + Console.WriteLine("z = " + solver.Value(z)); + } + } + + static void MinimalCpSatPrintIntermediateSolutions() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Creates the constraints. + model.Add(x != y); + // Create the objective. + model.Maximize(x + 2 * y + 3 * z); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithObjective cb = + new VarArraySolutionPrinterWithObjective(new IntVar[] {x, y, z}); + solver.SearchAllSolutions(model, cb); + Console.WriteLine(String.Format("Number of solutions found: {0}", + cb.SolutionCount())); + } + + static void MinimalCpSatAllSolutions() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Creates the constraints. + model.Add(x != y); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] {x, y, z}); + solver.SearchAllSolutions(model, cb); + Console.WriteLine(String.Format("Number of solutions found: {0}", + cb.SolutionCount())); + } + + static void Main() + { + Console.WriteLine("--- CodeSample ---"); + CodeSample(); + Console.WriteLine("--- LiteralSample ---"); + LiteralSample(); + Console.WriteLine("--- BoolOrSample ---"); + BoolOrSample(); + Console.WriteLine("--- ReifiedSample ---"); + ReifiedSample(); + Console.WriteLine("--- RabbitsAndPheasants ---"); + RabbitsAndPheasants(); + Console.WriteLine("--- BinpackingProblem ---"); + BinpackingProblem(); + Console.WriteLine("--- IntervalSample ---"); + IntervalSample(); + Console.WriteLine("--- OptionalIntervalSample ---"); + OptionalIntervalSample(); + Console.WriteLine("--- MinimalCpSat ---"); + MinimalCpSat(); + Console.WriteLine("--- MinimalCpSatWithTimeLimit ---"); + MinimalCpSatWithTimeLimit(); + Console.WriteLine("--- MinimalCpSatPrintIntermediateSolutions ---"); + MinimalCpSatPrintIntermediateSolutions(); + Console.WriteLine("--- MinimalCpSatAllSolutions ---"); + MinimalCpSatAllSolutions(); + } +} diff --git a/examples/dotnet/csharp/coins3.cs b/examples/dotnet/csharp/coins3.cs new file mode 100644 index 0000000000..de242f192d --- /dev/null +++ b/examples/dotnet/csharp/coins3.cs @@ -0,0 +1,114 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Coins3 +{ + + /** + * + * Coin application. + * + * From "Constraint Logic Programming using ECLiPSe" + * pages 99f and 234 ff. + * The solution in ECLiPSe is at page 236. + * + * """ + * What is the minimum number of coins that allows one to pay _exactly_ + * any amount smaller than one Euro? Recall that there are six different + * euro cents, of denomination 1, 2, 5, 10, 20, 50 + * """ + + * Also see http://www.hakank.org/or-tools/coins3.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Coins3"); + + // + // Data + // + int n = 6; // number of different coins + int[] variables = {1, 2, 5, 10, 25, 50}; + + IEnumerable RANGE = Enumerable.Range(0, n); + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 99, "x"); + IntVar num_coins = x.Sum().VarWithName("num_coins"); + + + // + // Constraints + // + + // Check that all changes from 1 to 99 can be made. + for(int j = 1; j < 100; j++) { + IntVar[] tmp = solver.MakeIntVarArray(n, 0, 99, "tmp"); + solver.Add(tmp.ScalProd(variables) == j); + + foreach(int i in RANGE) { + solver.Add(tmp[i] <= x[i]); + } + + } + + // + // Objective + // + OptimizeVar obj = num_coins.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_coins: {0}", num_coins.Value()); + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/coins_grid.cs b/examples/dotnet/csharp/coins_grid.cs new file mode 100644 index 0000000000..06eb73548f --- /dev/null +++ b/examples/dotnet/csharp/coins_grid.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class CoinsGrid +{ + + /** + * + * Solves the Coins Grid problm. + * See http://www.hakank.org/google_or_tools/coins_grid.py + * + */ + private static void Solve(int n = 31, int c = 14) + { + Solver solver = new Solver("CoinsGrid"); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 1 , "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // sum row/columns == c + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + col[j] = x[j,i]; + } + solver.Add(row.Sum() == c); + solver.Add(col.Sum() == c); + } + + // quadratic horizonal distance + IntVar[] obj_tmp = new IntVar[n * n]; + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + obj_tmp[i * n + j] = (x[i,j] * (i - j) * (i - j)).Var(); + } + } + IntVar obj_var = obj_tmp.Sum().Var(); + + // + // Objective + // + OptimizeVar obj = obj_var.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("obj: " + obj_var.Value()); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 31; + int c = 14; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + c = Convert.ToInt32(args[1]); + } + + + Solve(n, c); + } +} diff --git a/examples/dotnet/csharp/combinatorial_auction2.cs b/examples/dotnet/csharp/combinatorial_auction2.cs new file mode 100644 index 0000000000..c85d2acec1 --- /dev/null +++ b/examples/dotnet/csharp/combinatorial_auction2.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class CombinatorialAuction2 +{ + /** + * + * Combinatorial auction. + * + * This is a more general model for the combinatorial example + * in the Numberjack Tutorial, pages 9 and 24 (slides 19/175 and + * 51/175). + * + * See http://www.hakank.org/or-tools/combinatorial_auction2.py + * + * The original and more talkative model is here: + * http://www.hakank.org/numberjack/combinatorial_auction.py + * + */ + private static void Solve() + { + Solver solver = new Solver("CombinatorialAuction2"); + + // + // Data + // + int n = 5; + + // the items for each bid + int[][] items = { + new int[] {0,1}, // A,B + new int[] {0,2}, // A, C + new int[] {1,3}, // B,D + new int[] {1,2,3}, // B,C,D + new int[] {0} // A + }; + + int[] bid_ids = {0,1,2,3}; + int[] bid_amount = {10,20,30,40,14}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + IntVar z = x.ScalProd(bid_amount).VarWithName("z"); + + // + // Constraints + // + + foreach(int bid_id in bid_ids) { + + var tmp2 = (from item in Enumerable.Range(0, n) + from i in Enumerable.Range(0, items[item].Length) + where items[item][i] == bid_id + select x[item]); + + solver.Add(tmp2.ToArray().Sum() <= 1); + + } + + + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("z: {0,2} x: ", z.Value()); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/contiguity_regular.cs b/examples/dotnet/csharp/contiguity_regular.cs new file mode 100644 index 0000000000..a22cc5c290 --- /dev/null +++ b/examples/dotnet/csharp/contiguity_regular.cs @@ -0,0 +1,209 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ContiguityRegular +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + + } + + } + + + static void MyContiguity(Solver solver, IntVar[] x) { + + // the DFA (for regular) + int n_states = 3; + int input_max = 2; + int initial_state = 1; // note: state 0 is used for the failing state + // in MyRegular + + // all states are accepting states + int[] accepting_states = {1,2,3}; + + // The regular expression 0*1*0* + int[,] transition_fn = + { + {1,2}, // state 1 (start): input 0 -> state 1, input 1 -> state 2 i.e. 0* + {3,2}, // state 2: 1* + {3,0}, // state 3: 0* + }; + + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + } + + + /** + * + * Global constraint contiguity using regular + * + * This is a decomposition of the global constraint global contiguity. + * + * From Global Constraint Catalogue + * http://www.emn.fr/x-info/sdemasse/gccat/Cglobal_contiguity.html + * """ + * Enforce all variables of the VARIABLES collection to be assigned to 0 or 1. + * In addition, all variables assigned to value 1 appear contiguously. + * + * Example: + * (<0, 1, 1, 0>) + * + * The global_contiguity constraint holds since the sequence 0 1 1 0 contains + * no more than one group of contiguous 1. + * """ + * + * Also see http://www.hakank.org/or-tools/contiguity_regular.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ContiguityRegular"); + + // + // Data + // + int n = 7; // length of the array + + + // + // Decision variables + // + + // Note: We use 1..2 (instead of 0..1) and subtract 1 in the solution + IntVar[] reg_input = solver.MakeIntVarArray(n, 1, 2, "reg_input"); + + + // + // Constraints + // + MyContiguity(solver, reg_input); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(reg_input, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + // Note: here we subtract 1 to get 0..1 + Console.Write((reg_input[i].Value()-1) + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/contiguity_transition.cs b/examples/dotnet/csharp/contiguity_transition.cs new file mode 100644 index 0000000000..6d23d6a3f0 --- /dev/null +++ b/examples/dotnet/csharp/contiguity_transition.cs @@ -0,0 +1,123 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ContiguityRegular +{ + + static void MyContiguity(Solver solver, IntVar[] x) { + + // the DFA (for regular) + int initial_state = 1; + + // all states are accepting states + int[] accepting_states = {1,2,3}; + + // The regular expression 0*1*0* {state, input, next state} + int[,] transition_tuples = { {1, 0, 1}, + {1, 1, 2}, + {2, 0, 3}, + {2, 1, 2}, + {3, 0, 3} }; + + IntTupleSet result = new IntTupleSet(3); + result.InsertAll(transition_tuples); + + solver.Add(x.Transition(result, + initial_state, + accepting_states)); + } + + + /** + * + * Global constraint contiguity using Transition + * + * This version use the built-in TransitionConstraint. + * + * From Global Constraint Catalogue + * http://www.emn.fr/x-info/sdemasse/gccat/Cglobal_contiguity.html + * """ + * Enforce all variables of the VARIABLES collection to be assigned to 0 or 1. + * In addition, all variables assigned to value 1 appear contiguously. + * + * Example: + * (<0, 1, 1, 0>) + * + * The global_contiguity constraint holds since the sequence 0 1 1 0 contains + * no more than one group of contiguous 1. + * """ + * + * Also see http://www.hakank.org/or-tools/contiguity_regular.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ContiguityRegular"); + + // + // Data + // + int n = 7; // length of the array + + + // + // Decision variables + // + + IntVar[] reg_input = solver.MakeIntVarArray(n, 0, 1, "reg_input"); + + + // + // Constraints + // + MyContiguity(solver, reg_input); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(reg_input, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write((reg_input[i].Value()) + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/costas_array.cs b/examples/dotnet/csharp/costas_array.cs new file mode 100644 index 0000000000..060e72c46c --- /dev/null +++ b/examples/dotnet/csharp/costas_array.cs @@ -0,0 +1,181 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class CostasArray +{ + + /** + * + * Costas array + * + * From http://mathworld.wolfram.com/CostasArray.html: + * """ + * An order-n Costas array is a permutation on {1,...,n} such + * that the distances in each row of the triangular difference + * table are distinct. For example, the permutation {1,3,4,2,5} + * has triangular difference table {2,1,-2,3}, {3,-1,1}, {1,2}, + * and {4}. Since each row contains no duplications, the permutation + * is therefore a Costas array. + * """ + * + * Also see + * http://en.wikipedia.org/wiki/Costas_array + * http://hakank.org/or-tools/costas_array.py + * + */ + private static void Solve(int n = 6) + { + + Solver solver = new Solver("CostasArray"); + + // + // Data + // + Console.WriteLine("n: {0}", n); + + // + // Decision variables + // + IntVar[] costas = solver.MakeIntVarArray(n, 1, n, "costas"); + IntVar[,] differences = solver.MakeIntVarMatrix(n, n, -n+1, n-1, + "differences"); + + // + // Constraints + // + + // Fix the values in the lower triangle in the + // difference matrix to -n+1. This removes variants + // of the difference matrix for the the same Costas array. + for(int i = 0; i < n; i++) { + for(int j = 0; j <= i; j++ ) { + solver.Add(differences[i,j] == -n+1); + } + } + + // hakank: All the following constraints are from + // Barry O'Sullivans's original model. + // + solver.Add(costas.AllDifferent()); + + + // "How do the positions in the Costas array relate + // to the elements of the distance triangle." + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i < j) { + solver.Add( differences[i,j] - (costas[j] - costas[j-i-1]) == 0); + } + } + } + + + // "All entries in a particular row of the difference + // triangle must be distint." + for(int i = 0; i < n-2; i++) { + IntVar[] tmp = ( + from j in Enumerable.Range(0, n) + where j > i + select differences[i,j]).ToArray(); + solver.Add(tmp.AllDifferent()); + + } + + // + // "All the following are redundant - only here to speed up search." + // + + // "We can never place a 'token' in the same row as any other." + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i < j) { + solver.Add(differences[i,j] != 0); + solver.Add(differences[i,j] != 0); + } + } + } + + for(int k = 2; k < n; k++) { + for(int l = 2; l < n; l++) { + if (k < l) { + solver.Add( + (differences[k-2,l-1] + differences[k,l]) - + (differences[k-1,l-1] + differences[k-1,l]) == 0 + ); + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(costas, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("costas: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", costas[i].Value()); + } + Console.WriteLine("\ndifferences:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + long v = differences[i,j].Value(); + if (v == -n+1) { + Console.Write(" "); + } else { + Console.Write("{0,2} ", v); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int n = 6; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + + } +} diff --git a/examples/dotnet/csharp/covering_opl.cs b/examples/dotnet/csharp/covering_opl.cs new file mode 100644 index 0000000000..39da44748b --- /dev/null +++ b/examples/dotnet/csharp/covering_opl.cs @@ -0,0 +1,128 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCoveringOPL +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering_opl.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringOPL"); + + // + // data + // + int num_workers = 32; + int num_tasks = 15; + + // Which worker is qualified for each task. + // Note: This is 1-based and will be made 0-base below. + int[][] qualified = { + new int[] { 1, 9, 19, 22, 25, 28, 31 }, + new int[] { 2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32 }, + new int[] { 3, 10, 19, 24, 26, 30, 32 }, + new int[] { 4, 21, 25, 28, 32 }, + new int[] { 5, 11, 16, 22, 23, 27, 31 }, + new int[] { 6, 20, 24, 26, 30, 32 }, + new int[] { 7, 12, 17, 25, 30, 31 } , + new int[] { 8, 17, 20, 22, 23 }, + new int[] { 9, 13, 14, 26, 29, 30, 31 }, + new int[] { 10, 21, 25, 31, 32 }, + new int[] { 14, 15, 18, 23, 24, 27, 30, 32 }, + new int[] { 18, 19, 22, 24, 26, 29, 31 }, + new int[] { 11, 20, 25, 28, 30, 32 }, + new int[] { 16, 19, 23, 31 }, + new int[] { 9, 18, 26, 28, 31, 32 } + }; + + int[] cost = {1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9}; + + + // + // Decision variables + // + IntVar[] hire = solver.MakeIntVarArray(num_workers, 0, 1, "workers"); + IntVar total_cost = hire.ScalProd(cost).Var(); + + // + // Constraints + // + + for(int j = 0; j < num_tasks; j++) { + // Sum the cost for hiring the qualified workers + // (also, make 0-base). + int len = qualified[j].Length; + IntVar[] tmp = new IntVar[len]; + for(int c = 0; c < len; c++) { + tmp[c] = hire[qualified[j][c] - 1]; + } + solver.Add(tmp.Sum() >= 1); + } + + + // + // objective + // + OptimizeVar objective = total_cost.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(hire, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("Cost: " + total_cost.Value()); + Console.Write("Hire: "); + for(int i = 0; i < num_workers; i++) { + if (hire[i].Value() == 1) { + Console.Write(i + " "); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/crew.cs b/examples/dotnet/csharp/crew.cs new file mode 100644 index 0000000000..27965dafdf --- /dev/null +++ b/examples/dotnet/csharp/crew.cs @@ -0,0 +1,272 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Crew +{ + + /** + * + * Crew allocation problem in Google CP Solver. + * + * From Gecode example crew + * examples/crew.cc + * """ + * Example: Airline crew allocation + * + * Assign 20 flight attendants to 10 flights. Each flight needs a certain + * number of cabin crew, and they have to speak certain languages. + * Every cabin crew member has two flights off after an attended flight. + * """ + * + * Also see http://www.hakank.org/or-tools/crew.pl + * + */ + private static void Solve(int sols = 1, int minimize = 0) + { + Solver solver = new Solver("Crew"); + + // + // Data + // + string[] names = {"Tom", + "David", + "Jeremy", + "Ron", + "Joe", + "Bill", + "Fred", + "Bob", + "Mario", + "Ed", + "Carol", + "Janet", + "Tracy", + "Marilyn", + "Carolyn", + "Cathy", + "Inez", + "Jean", + "Heather", + "Juliet"}; + + int num_persons = names.Length; + + + // + // Attributes of the crew + // + int[,] attributes = { + // steward, hostess, french, spanish, german + {1,0,0,0,1}, // Tom = 0 + {1,0,0,0,0}, // David = 1 + {1,0,0,0,1}, // Jeremy = 2 + {1,0,0,0,0}, // Ron = 3 + {1,0,0,1,0}, // Joe = 4 + {1,0,1,1,0}, // Bill = 5 + {1,0,0,1,0}, // Fred = 6 + {1,0,0,0,0}, // Bob = 7 + {1,0,0,1,1}, // Mario = 8 + {1,0,0,0,0}, // Ed = 9 + {0,1,0,0,0}, // Carol = 10 + {0,1,0,0,0}, // Janet = 11 + {0,1,0,0,0}, // Tracy = 12 + {0,1,0,1,1}, // Marilyn = 13 + {0,1,0,0,0}, // Carolyn = 14 + {0,1,0,0,0}, // Cathy = 15 + {0,1,1,1,1}, // Inez = 16 + {0,1,1,0,0}, // Jean = 17 + {0,1,0,1,1}, // Heather = 18 + {0,1,1,0,0} // Juliet = 19 + }; + + + // + // Required number of crew members. + // + // The columns are in the following order: + // staff : Overall number of cabin crew needed + // stewards : How many stewards are required + // hostesses : How many hostesses are required + // french : How many French speaking employees are required + // spanish : How many Spanish speaking employees are required + // german : How many German speaking employees are required + // + int[,] required_crew = { + {4,1,1,1,1,1}, // Flight 1 + {5,1,1,1,1,1}, // Flight 2 + {5,1,1,1,1,1}, // .. + {6,2,2,1,1,1}, + {7,3,3,1,1,1}, + {4,1,1,1,1,1}, + {5,1,1,1,1,1}, + {6,1,1,1,1,1}, + {6,2,2,1,1,1}, // ... + {7,3,3,1,1,1} // Flight 10 + }; + + int num_flights = required_crew.GetLength(0); + + + // + // Decision variables + // + IntVar[,] crew = solver.MakeIntVarMatrix(num_flights, num_persons, + 0, 1, "crew"); + IntVar[] crew_flat = crew.Flatten(); + + // number of working persons + IntVar num_working = solver.MakeIntVar(1, num_persons, "num_working"); + + // + // Constraints + // + + // number of working persons + IntVar[] nw = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + IntVar[] tmp = new IntVar[num_flights]; + for(int f = 0; f < num_flights; f++) { + tmp[f] = crew[f,p]; + } + nw[p] = tmp.Sum() > 0; + } + solver.Add(nw.Sum() == num_working); + + for(int f = 0; f < num_flights; f++) { + // size of crew + IntVar[] tmp = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + tmp[p] = crew[f,p]; + } + solver.Add(tmp.Sum() == required_crew[f,0]); + + // attributes and requirements + for(int a = 0; a < 5; a++) { + IntVar[] tmp2 = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + tmp2[p] = (crew[f,p]*attributes[p,a]).Var(); + } + solver.Add(tmp2.Sum() >= required_crew[f,a+1]); + } + } + + // after a flight, break for at least two flights + for(int f = 0; f < num_flights - 2; f++) { + for(int i = 0; i < num_persons; i++) { + solver.Add(crew[f,i] + crew[f+1,i] + crew[f+2,i] <= 1); + } + } + + // extra contraint: all must work at least two of the flights + /* + for(int p = 0; p < num_persons; p++) { + IntVar[] tmp = new IntVar[num_flights]; + for(int f = 0; f < num_flights; f++) { + tmp[f] = crew[f,p]; + } + solver.Add(tmp.Sum() >= 2); + } + */ + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(crew_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (minimize > 0) { + OptimizeVar obj = num_working.Minimize(1); + solver.NewSearch(db, obj); + } else { + solver.NewSearch(db); + } + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + Console.WriteLine("Solution #{0}", num_solutions); + Console.WriteLine("Number working: {0}", num_working.Value()); + + for(int f = 0; f < num_flights; f++) { + for(int p = 0; p < num_persons; p++) { + Console.Write(crew[f,p].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("\nFlights: "); + for(int f = 0; f < num_flights; f++) { + Console.Write("Flight #{0}: ", f); + for(int p = 0; p < num_persons; p++) { + if (crew[f, p].Value() == 1) { + Console.Write(names[p] + " "); + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nCrew:"); + for(int p = 0; p < num_persons; p++) { + Console.Write("{0,-10}", names[p]); + for(int f = 0; f < num_flights; f++) { + if (crew[f,p].Value() == 1) { + Console.Write(f + " "); + } + } + Console.WriteLine(); + } + + Console.WriteLine(); + + if (num_solutions >= sols) { + break; + } + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 1; + int min = 0; // > 0 -> minimize num_working + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + min = Convert.ToInt32(args[1]); + } + + Solve(n, min); + } +} diff --git a/examples/dotnet/csharp/crossword.cs b/examples/dotnet/csharp/crossword.cs new file mode 100644 index 0000000000..4c5e5ee2b8 --- /dev/null +++ b/examples/dotnet/csharp/crossword.cs @@ -0,0 +1,188 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +// Note: During compilation, there are a couple of +// warnings about assigned but never used variables. +// It's the characters a..z so it's quite benign. + + +public class Crossword +{ + + /** + * + * Solving a simple crossword. + * See http://www.hakank.org/or-tools/crossword2.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("Crossword"); + + // + // data + // + String[] alpha = {"_","a","b","c","d","e","f", + "g","h","i","j","k","l","m", + "n","o","p","q","r","s","t", + "u","v","w","x","y","z"}; + + int a=1; int b=2; int c=3; int d=4; int e=5; int f=6; + int g=7; int h=8; int i=9; int j=10; int k=11; int l=12; + int m=13; int n=14; int o=15; int p=16; int q=17; int r=18; + int s=19; int t=20; int u=21; int v=22; int w=23; int x=24; + int y=25; int z=26; + + const int num_words = 15; + int word_len = 5; + + int[,] AA = {{h, o, s, e, s}, // HOSES + {l, a, s, e, r}, // LASER + {s, a, i, l, s}, // SAILS + {s, h, e, e, t}, // SHEET + {s, t, e, e, r}, // STEER + {h, e, e, l, 0}, // HEEL + {h, i, k, e, 0}, // HIKE + {k, e, e, l, 0}, // KEEL + {k, n, o, t, 0}, // KNOT + {l, i, n, e, 0}, // LINE + {a, f, t, 0, 0}, // AFT + {a, l, e, 0, 0}, // ALE + {e, e, l, 0, 0}, // EEL + {l, e, e, 0, 0}, // LEE + {t, i, e, 0, 0}}; // TIE + + int num_overlapping = 12; + int[,] overlapping = {{0, 2, 1, 0}, // s + {0, 4, 2, 0}, // s + + {3, 1, 1, 2}, // i + {3, 2, 4, 0}, // k + {3, 3, 2, 2}, // e + + {6, 0, 1, 3}, // l + {6, 1, 4, 1}, // e + {6, 2, 2, 3}, // e + + {7, 0, 5, 1}, // l + {7, 2, 1, 4}, // s + {7, 3, 4, 2}, // e + {7, 4, 2, 4}}; // r + + int N = 8; + + // + // Decision variables + // + // for labeling on A and E + IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, + 0, 26, "A"); + IntVar[] A_flat = A.Flatten(); + IntVar[] all = new IntVar[(num_words * word_len) + N]; + for(int I = 0; I < num_words; I++) { + for(int J = 0; J < word_len; J++) { + all[I * word_len + J] = A[I,J]; + } + } + + + + IntVar[] E = solver.MakeIntVarArray(N, 0, num_words, "E"); + for(int I = 0; I < N; I++) { + all[num_words * word_len + I] = E[I]; + } + + + + // + // Constraints + // + solver.Add(E.AllDifferent()); + + for(int I = 0; I < num_words; I++) { + for(int J = 0; J < word_len; J++) { + solver.Add(A[I,J] == AA[I,J]); + } + } + + // This contraint handles the overlappings. + // + // It's coded in MiniZinc as + // + // forall(i in 1..num_overlapping) ( + // A[E[overlapping[i,1]], overlapping[i,2]] = + // A[E[overlapping[i,3]], overlapping[i,4]] + // ) + // and in or-tools/Python as + // solver.Add( + // solver.Element(A_flat,E[overlapping[I][0]]*word_len+overlapping[I][1]) + // == + // solver.Element(A_flat,E[overlapping[I][2]]*word_len+overlapping[I][3])) + // + for(int I = 0; I < num_overlapping; I++) { + solver.Add( + A_flat.Element(E[overlapping[I,0]] * word_len + overlapping[I,1]) == + A_flat.Element(E[overlapping[I,2]] * word_len + overlapping[I,3])); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("E: "); + for(int ee = 0; ee < N; ee++) { + int e_val = (int)E[ee].Value(); + Console.Write(ee + ": (" + e_val + ") "); + for(int ii = 0; ii < word_len; ii++) { + Console.Write(alpha[(int)A[ee,ii].Value()]); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/crypta.cs b/examples/dotnet/csharp/crypta.cs new file mode 100644 index 0000000000..eb6d9f45fa --- /dev/null +++ b/examples/dotnet/csharp/crypta.cs @@ -0,0 +1,119 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Crypta +{ + /** + * + * Cryptarithmetic puzzle. + * + * Prolog benchmark problem GNU Prolog (crypta.pl) + * """ + * Name : crypta.pl + * Title : crypt-arithmetic + * Original Source: P. Van Hentenryck's book + * Adapted by : Daniel Diaz - INRIA France + * Date : September 1992 + * + * Solve the operation: + * + * B A I J J A J I I A H F C F E B B J E A + * + D H F G A B C D I D B I F F A G F E J E + * ----------------------------------------- + * = G J E G A C D D H F A F J B F I H E E F + * """ + * + * + * Also see http://hakank.org/or-tools/crypta.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Crypta"); + + // + // Decision variables + // + IntVar A = solver.MakeIntVar(0, 9, "A"); + IntVar B = solver.MakeIntVar(0, 9, "B"); + IntVar C = solver.MakeIntVar(0, 9, "C"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar F = solver.MakeIntVar(0, 9, "F"); + IntVar G = solver.MakeIntVar(0, 9, "G"); + IntVar H = solver.MakeIntVar(0, 9, "H"); + IntVar I = solver.MakeIntVar(0, 9, "I"); + IntVar J = solver.MakeIntVar(0, 9, "J"); + + IntVar[] LD = new IntVar[] {A,B,C,D,E,F,G,H,I,J}; + + IntVar Sr1 = solver.MakeIntVar(0, 1, "Sr1"); + IntVar Sr2 = solver.MakeIntVar(0, 1, "Sr2"); + + + // + // Constraints + // + solver.Add(LD.AllDifferent()); + solver.Add(B >= 1); + solver.Add(D >= 1); + solver.Add(G >= 1); + + solver.Add((A+10*E+100*J+1000*B+10000*B+100000*E+1000000*F+ + E+10*J+100*E+1000*F+10000*G+100000*A+1000000*F) == + (F+10*E+100*E+1000*H+10000*I+100000*F+1000000*B+10000000*Sr1)); + + + solver.Add((C+10*F+100*H+1000*A+10000*I+100000*I+1000000*J+ + F+10*I+100*B+1000*D+10000*I+100000*D+1000000*C+Sr1) == + (J+10*F+100*A+1000*F+10000*H+100000*D+1000000*D+10000000*Sr2)); + + + solver.Add((A+10*J+100*J+1000*I+10000*A+100000*B+ + B+10*A+100*G+1000*F+10000*H+100000*D+Sr2) == + (C+10*A+100*G+1000*E+10000*J+100000*G)); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(LD, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < 10; i++) { + Console.Write(LD[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/crypto.cs b/examples/dotnet/csharp/crypto.cs new file mode 100644 index 0000000000..6d31677209 --- /dev/null +++ b/examples/dotnet/csharp/crypto.cs @@ -0,0 +1,152 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Crypto +{ + /** + * + * Crypto problem. + * + * This is the standard benchmark "crypto" problem. + * + * From GLPK:s model cryto.mod. + * + * """ + * This problem comes from the newsgroup rec.puzzle. + * The numbers from 1 to 26 are assigned to the letters of the alphabet. + * The numbers beside each word are the total of the values assigned to + * the letters in the word (e.g. for LYRE: L, Y, R, E might be to equal + * 5, 9, 20 and 13, or any other combination that add up to 47). + * Find the value of each letter under the equations: + * + * BALLET 45 GLEE 66 POLKA 59 SONG 61 + * CELLO 43 JAZZ 58 QUARTET 50 SOPRANO 82 + * CONCERT 74 LYRE 47 SAXOPHONE 134 THEME 72 + * FLUTE 30 OBOE 53 SCALE 51 VIOLIN 100 + * FUGUE 50 OPERA 65 SOLO 37 WALTZ 34 + * + * Solution: + * A, B,C, D, E,F, G, H, I, J, K,L,M, N, O, P,Q, R, S,T,U, V,W, X, Y, Z + * 5,13,9,16,20,4,24,21,25,17,23,2,8,12,10,19,7,11,15,3,1,26,6,22,14,18 + * + * Reference: + * Koalog Constraint Solver , + * Simple problems, the crypto-arithmetic puzzle ALPHACIPHER. + * """ + * + * Also see http://hakank.org/or-tools/crypto.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Crypto"); + + int num_letters = 26; + + int BALLET = 45; + int CELLO = 43; + int CONCERT = 74; + int FLUTE = 30; + int FUGUE = 50; + int GLEE = 66; + int JAZZ = 58; + int LYRE = 47; + int OBOE = 53; + int OPERA = 65; + int POLKA = 59; + int QUARTET = 50; + int SAXOPHONE = 134; + int SCALE = 51; + int SOLO = 37; + int SONG = 61; + int SOPRANO = 82; + int THEME = 72; + int VIOLIN = 100; + int WALTZ = 34; + + + // + // Decision variables + // + IntVar[] LD = solver.MakeIntVarArray(num_letters, 1, num_letters, "LD"); + + // Note D is not used in the constraints below + IntVar A = LD[0]; IntVar B = LD[1]; IntVar C = LD[2]; // IntVar D = LD[3]; + IntVar E = LD[4]; IntVar F = LD[5]; IntVar G = LD[6]; IntVar H = LD[7]; + IntVar I = LD[8]; IntVar J = LD[9]; IntVar K = LD[10]; IntVar L = LD[11]; + IntVar M = LD[12]; IntVar N = LD[13]; IntVar O = LD[14]; IntVar P = LD[15]; + IntVar Q = LD[16]; IntVar R = LD[17]; IntVar S = LD[18]; IntVar T = LD[19]; + IntVar U = LD[20]; IntVar V = LD[21]; IntVar W = LD[22]; IntVar X = LD[23]; + IntVar Y = LD[24]; IntVar Z = LD[25]; + + // + // Constraints + // + solver.Add(LD.AllDifferent()); + solver.Add( B + A + L + L + E + T == BALLET); + solver.Add( C + E + L + L + O == CELLO); + solver.Add( C + O + N + C + E + R + T == CONCERT); + solver.Add( F + L + U + T + E == FLUTE); + solver.Add( F + U + G + U + E == FUGUE); + solver.Add( G + L + E + E == GLEE); + solver.Add( J + A + Z + Z == JAZZ); + solver.Add( L + Y + R + E == LYRE); + solver.Add( O + B + O + E == OBOE); + solver.Add( O + P + E + R + A == OPERA); + solver.Add( P + O + L + K + A == POLKA); + solver.Add( Q + U + A + R + T + E + T == QUARTET); + solver.Add(S + A + X + O + P + H + O + N + E == SAXOPHONE); + solver.Add( S + C + A + L + E == SCALE); + solver.Add( S + O + L + O == SOLO); + solver.Add( S + O + N + G == SONG); + solver.Add( S + O + P + R + A + N + O == SOPRANO); + solver.Add( T + H + E + M + E == THEME); + solver.Add( V + I + O + L + I + N == VIOLIN); + solver.Add( W + A + L + T + Z == WALTZ); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(LD, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + while (solver.NextSolution()) { + for(int i = 0; i < num_letters; i++) { + Console.WriteLine("{0}: {1,2}", str[i], LD[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/cscvrptw.cs b/examples/dotnet/csharp/cscvrptw.cs new file mode 100644 index 0000000000..016e2ac9ca --- /dev/null +++ b/examples/dotnet/csharp/cscvrptw.cs @@ -0,0 +1,355 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +/// +/// Sample showing how to model and solve a capacitated vehicle routing +/// problem with time windows using the swig-wrapped version of the vehicle +/// routing library in src/constraint_solver. +/// +public class CapacitatedVehicleRoutingProblemWithTimeWindows { + + /// + /// A position on the map with (x, y) coordinates. + /// + class Position { + public Position() { + this.x_ = 0; + this.y_ = 0; + } + + public Position(int x, int y) { + this.x_ = x; + this.y_ = y; + } + + public int x_; + public int y_; + } + + /// + /// A time window with start/end data. + /// + class TimeWindow { + public TimeWindow() { + this.start_ = -1; + this.end_ = -1; + } + + public TimeWindow(int start, int end) { + this.start_ = start; + this.end_ = end; + } + + public int start_; + public int end_; + } + + /// + /// Manhattan distance implemented as a callback. It uses an array of + /// positions and computes the Manhattan distance between the two + /// positions of two different indices. + /// + class Manhattan : NodeEvaluator2 { + public Manhattan(Position[] locations, int coefficient) { + this.locations_ = locations; + this.coefficient_ = coefficient; + } + + public override long Run(int first_index, int second_index) { + if (first_index >= locations_.Length || + second_index >= locations_.Length) { + return 0; + } + return (Math.Abs(locations_[first_index].x_ - + locations_[second_index].x_) + + Math.Abs(locations_[first_index].y_ - + locations_[second_index].y_)) * coefficient_; + } + + private Position[] locations_; + private int coefficient_; + }; + + /// + /// A callback that computes the volume of a demand stored in an + /// integer array. + /// + class Demand : NodeEvaluator2 { + public Demand(int[] order_demands) { + this.order_demands_ = order_demands; + } + + public override long Run(int first_index, int second_index) { + if (first_index < order_demands_.Length) { + return order_demands_[first_index]; + } + return 0; + } + + private int[] order_demands_; + }; + + /// Locations representing either an order location or a vehicle route + /// start/end. + private Position[] locations_; + /// Quantity to be picked up for each order. + private int[] order_demands_; + /// Time window in which each order must be performed. + private TimeWindow[] order_time_windows_; + /// Penalty cost "paid" for dropping an order. + private int[] order_penalties_; + /// Capacity of the vehicles. + private int vehicle_capacity_ = 0; + /// Latest time at which each vehicle must end its tour. + private int[] vehicle_end_time_; + /// Cost per unit of distance of each vehicle. + private int[] vehicle_cost_coefficients_; + /// Vehicle start and end indices. They have to be implemented as int[] due + /// to the available SWIG-ed interface. + private int[] vehicle_starts_; + private int[] vehicle_ends_; + + /// Random number generator to produce data. + private Random random_generator = new Random(0xBEEF); + + /// + /// Constructs a capacitated vehicle routing problem with time windows. + /// + private CapacitatedVehicleRoutingProblemWithTimeWindows() {} + + /// + /// Creates order data. Location of the order is random, as well + /// as its demand (quantity), time window and penalty. /// + /// + /// number of orders to build. + /// maximum x coordinate in which orders are located. + /// + /// maximum y coordinate in which orders are located. + /// + /// maximum quantity of a demand. + /// maximum starting time of the order time + /// window. + /// duration of the order time window. + /// + /// minimum pernalty cost if order is dropped. + /// + /// maximum pernalty cost if order is dropped. + /// + private void BuildOrders(int number_of_orders, + int number_of_vehicles, + int x_max, int y_max, + int demand_max, + int time_window_max, + int time_window_width, + int penalty_min, + int penalty_max) { + Console.WriteLine("Building orders."); + locations_ = new Position[number_of_orders + 2 * number_of_vehicles]; + order_demands_ = new int[number_of_orders]; + order_time_windows_ = new TimeWindow[number_of_orders]; + order_penalties_ = new int[number_of_orders]; + for (int order = 0; order < number_of_orders; ++order) { + locations_[order] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + order_demands_[order] = random_generator.Next(demand_max + 1); + int time_window_start = random_generator.Next(time_window_max + 1); + order_time_windows_[order] = + new TimeWindow(time_window_start, + time_window_start + time_window_width); + order_penalties_[order] = + random_generator.Next(penalty_max - penalty_min + 1) + penalty_min; + } + } + + /// + /// Creates fleet data. Vehicle starting and ending locations are + /// random, as well as vehicle costs per distance unit. + /// + /// + /// number of orders + /// number of vehicles + /// maximum x coordinate in which orders are located. + /// + /// maximum y coordinate in which orders are located. + /// + /// latest end time of a tour of a vehicle. + /// capacity of a vehicle. + /// maximum cost per distance unit of a + /// vehicle (minimum is 1) + private void BuildFleet(int number_of_orders, + int number_of_vehicles, + int x_max, int y_max, + int end_time, + int capacity, + int cost_coefficient_max) { + Console.WriteLine("Building fleet."); + vehicle_capacity_ = capacity; + vehicle_starts_ = new int[number_of_vehicles]; + vehicle_ends_ = new int[number_of_vehicles]; + vehicle_end_time_ = new int[number_of_vehicles]; + vehicle_cost_coefficients_ = new int[number_of_vehicles]; + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + int index = 2 * vehicle + number_of_orders; + vehicle_starts_[vehicle] = index; + locations_[index] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + vehicle_ends_[vehicle] = index + 1; + locations_[index + 1] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + vehicle_end_time_[vehicle] = end_time; + vehicle_cost_coefficients_[vehicle] = + random_generator.Next(cost_coefficient_max) + 1; + } + } + + /// + /// Solves the current routing problem. + /// + private void Solve(int number_of_orders, int number_of_vehicles) { + Console.WriteLine("Creating model with " + number_of_orders + + " orders and " + number_of_vehicles + " vehicles."); + // Finalizing model + int number_of_locations = locations_.Length; + + RoutingModel model = + new RoutingModel(number_of_locations, number_of_vehicles, + vehicle_starts_, vehicle_ends_); + + // Setting up dimensions + const int big_number = 100000; + NodeEvaluator2 manhattan_callback = new Manhattan(locations_, 1); + model.AddDimension( + manhattan_callback, big_number, big_number, false, "time"); + NodeEvaluator2 demand_callback = new Demand(order_demands_); + model.AddDimension(demand_callback, 0, vehicle_capacity_, true, "capacity"); + + // Setting up vehicles + NodeEvaluator2[] cost_callbacks = new NodeEvaluator2[number_of_vehicles]; + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + int cost_coefficient = vehicle_cost_coefficients_[vehicle]; + NodeEvaluator2 manhattan_cost_callback = + new Manhattan(locations_, cost_coefficient); + cost_callbacks[vehicle] = manhattan_cost_callback; + model.SetVehicleCost(vehicle, manhattan_cost_callback); + model.CumulVar(model.End(vehicle), "time").SetMax( + vehicle_end_time_[vehicle]); + } + + // Setting up orders + for (int order = 0; order < number_of_orders; ++order) { + model.CumulVar(order, "time").SetRange(order_time_windows_[order].start_, + order_time_windows_[order].end_); + int[] orders = {order}; + model.AddDisjunction(orders, order_penalties_[order]); + } + + // Solving + RoutingSearchParameters search_parameters = + RoutingModel.DefaultSearchParameters(); + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.AllUnperformed; + + Console.WriteLine("Search"); + Assignment solution = model.SolveWithParameters(search_parameters); + + //protect callbacks from the GC + GC.KeepAlive(manhattan_callback); + GC.KeepAlive(demand_callback); + for (int cost_callback_index = 0; cost_callback_index < cost_callbacks.Length; cost_callback_index++) { + GC.KeepAlive(cost_callbacks[cost_callback_index]); + } + + if (solution != null) { + String output = "Total cost: " + solution.ObjectiveValue() + "\n"; + // Dropped orders + String dropped = ""; + for (int order = 0; order < number_of_orders; ++order) { + if (solution.Value(model.NextVar(order)) == order) { + dropped += " " + order; + } + } + if (dropped.Length > 0) { + output += "Dropped orders:" + dropped + "\n"; + } + // Routes + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + String route = "Vehicle " + vehicle + ": "; + long order = model.Start(vehicle); + if (model.IsEnd(solution.Value(model.NextVar(order)))) { + route += "Empty"; + } else { + for (; + !model.IsEnd(order); + order = solution.Value(model.NextVar(order))) { + IntVar local_load = model.CumulVar(order, "capacity"); + IntVar local_time = model.CumulVar(order, "time"); + route += order + " Load(" + solution.Value(local_load) + ") " + + "Time(" + solution.Min(local_time) + ", " + + solution.Max(local_time) + ") -> "; + } + IntVar load = model.CumulVar(order, "capacity"); + IntVar time = model.CumulVar(order, "time"); + route += order + " Load(" + solution.Value(load) + ") " + + "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")"; + } + output += route + "\n"; + } + Console.WriteLine(output); + } + } + + + public static void Main(String[] args) + { + CapacitatedVehicleRoutingProblemWithTimeWindows problem = + new CapacitatedVehicleRoutingProblemWithTimeWindows(); + int x_max = 20; + int y_max = 20; + int demand_max = 3; + int time_window_max = 24 * 60; + int time_window_width = 4 * 60; + int penalty_min = 50; + int penalty_max = 100; + int end_time = 24 * 60; + int cost_coefficient_max = 3; + + int orders = 100; + int vehicles = 20; + int capacity = 50; + + problem.BuildOrders(orders, + vehicles, + x_max, + y_max, + demand_max, + time_window_max, + time_window_width, + penalty_min, + penalty_max); + problem.BuildFleet(orders, + vehicles, + x_max, + y_max, + end_time, + capacity, + cost_coefficient_max); + problem.Solve(orders, vehicles); + } +} diff --git a/examples/dotnet/csharp/csflow.cs b/examples/dotnet/csharp/csflow.cs new file mode 100644 index 0000000000..16bd64532e --- /dev/null +++ b/examples/dotnet/csharp/csflow.cs @@ -0,0 +1,108 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.Graph; + +public class CsFlow +{ + private static void SolveMaxFlow() + { + Console.WriteLine("Max Flow Problem"); + int numNodes = 6; + int numArcs = 9; + int[] tails = {0, 0, 0, 0, 1, 2, 3, 3, 4}; + int[] heads = {1, 2, 3, 4, 3, 4, 4, 5, 5}; + int[] capacities = {5, 8, 5, 3, 4, 5, 6, 6, 4}; + int[] expectedFlows = {4, 4, 2, 0, 4, 4, 0, 6, 4}; + int expectedTotalFlow = 10; + MaxFlow maxFlow = new MaxFlow(); + for (int i = 0; i < numArcs; ++i) + { + int arc = maxFlow.AddArcWithCapacity(tails[i], heads[i], capacities[i]); + if (arc != i) throw new Exception("Internal error"); + } + int source = 0; + int sink = numNodes - 1; + Console.WriteLine("Solving max flow with " + numNodes + " nodes, and " + + numArcs + " arcs, source=" + source + ", sink=" + sink); + int solveStatus = maxFlow.Solve(source, sink); + if (solveStatus == MaxFlow.OPTIMAL) + { + long totalFlow = maxFlow.OptimalFlow(); + Console.WriteLine("total computed flow " + totalFlow + + ", expected = " + expectedTotalFlow); + for (int i = 0; i < numArcs; ++i) + { + Console.WriteLine("Arc " + i + " (" + maxFlow.Head(i) + " -> " + + maxFlow.Tail(i) + "), capacity = " + + maxFlow.Capacity(i) + ") computed = " + + maxFlow.Flow(i) + ", expected = " + expectedFlows[i]); + } + } + else + { + Console.WriteLine("Solving the max flow problem failed. Solver status: " + + solveStatus); + } + } + + private static void SolveMinCostFlow() + { + Console.WriteLine("Min Cost Flow Problem"); + int numSources = 4; + int numTargets = 4; + int[,] costs = { {90, 75, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115} }; + int expectedCost = 275; + MinCostFlow minCostFlow = new MinCostFlow(); + for (int source = 0; source < numSources; ++source) + { + for (int target = 0; target < numTargets; ++target) { + minCostFlow.AddArcWithCapacityAndUnitCost( + source, /*target=*/numSources + target, /*capacity=*/1, + /*flow unit cost=*/costs[source, target]); + } + } + for (int source = 0; source < numSources; ++source) + { + minCostFlow.SetNodeSupply(source, 1); + } + for (int target = 0; target < numTargets; ++target) + { + minCostFlow.SetNodeSupply(numSources + target, -1); + } + Console.WriteLine("Solving min cost flow with " + numSources + + " sources, and " + numTargets + " targets."); + int solveStatus = minCostFlow.Solve(); + if (solveStatus == MinCostFlow.OPTIMAL) + { + Console.WriteLine("total computed flow cost = " + + minCostFlow.OptimalCost() + + ", expected = " + expectedCost); + } + else + { + Console.WriteLine("Solving the min cost flow problem failed." + + " Solver status: " + solveStatus); + } + } + + static void Main() + { + SolveMaxFlow(); + SolveMinCostFlow(); + } +} diff --git a/examples/dotnet/csharp/csfz.cs b/examples/dotnet/csharp/csfz.cs new file mode 100644 index 0000000000..008d738c4b --- /dev/null +++ b/examples/dotnet/csharp/csfz.cs @@ -0,0 +1,95 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.Flatzinc; + +public class CsFz +{ + /** + * Loads a flatzinc file (passed as the first argument) and solves it. + */ + private static void Solve(String filename) + { + Model model = new Model(filename); + model.LoadFromFile(filename); + // Uncomment to see the model. + // Console.WriteLine(model.ToString()); + // This is mandatory. + model.PresolveForCp(/*verbose=*/false); + // Display basic statistics on the model. + model.PrintStatistics(); + + FlatzincParameters parameters = new FlatzincParameters(); + // Initialize to default values as in the C++ runner. + parameters.all_solutions = false; + parameters.free_search = false; + parameters.last_conflict = false; + parameters.heuristic_period = 100; + parameters.ignore_unknown = false; + parameters.log_period = 10000000; + parameters.luby_restart = -1; + parameters.num_solutions = 0; + parameters.restart_log_size = -1; + parameters.threads = 0; + parameters.time_limit_in_ms = 10000; + parameters.logging = false; + parameters.verbose_impact = false; + parameters.thread_id = -1; + parameters.search_type = FlatzincParameters.DEFAULT; + // Mandatory to retrieve solutions. + parameters.store_all_solutions = true; + + Solver solver = new Solver(model); + solver.Solve(parameters); + + int last = solver.NumStoredSolutions() - 1; + if (last >= 0) { + SolutionOutputSpecsVector output_vector = model.output(); + foreach (SolutionOutputSpecs output in output_vector) { + if (output.variable != null) { + IntegerVariable var = output.variable; + Console.WriteLine(output.name + " = " + + solver.StoredValue(last, var)); + } + if (output.flat_variables.Count > 0) { + String line = output.name; + foreach (SolutionOutputSpecs.Bounds b in output.bounds) { + line += "[" + b.ToString() + "]"; + } + line += " = {"; + bool start = true; + foreach (IntegerVariable var in output.flat_variables) { + if (start) { + start = false; + } else { + line += ", "; + } + line += solver.StoredValue(last, var); + } + line += "}"; + Console.WriteLine(line); + } + } + } + } + + public static void Main(String[] args) + { + if (args.Length == 0) { + Console.WriteLine("A file name is required!"); + } else { + Solve(args[0]); + } + } +} diff --git a/examples/dotnet/csharp/csintegerprogramming.cs b/examples/dotnet/csharp/csintegerprogramming.cs new file mode 100644 index 0000000000..b05e68bb1f --- /dev/null +++ b/examples/dotnet/csharp/csintegerprogramming.cs @@ -0,0 +1,124 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.LinearSolver; + +public class CsIntegerProgramming +{ + private static void RunIntegerProgrammingExample(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1 and x2 are integer non-negative variables. + Variable x1 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x2"); + + // Minimize x1 + 2 * x2. + Objective objective = solver.Objective(); + objective.SetMinimization(); + objective.SetCoefficient(x1, 1); + objective.SetCoefficient(x2, 2); + + // 2 * x2 + 3 * x1 >= 17. + Constraint ct = solver.MakeConstraint(17, double.PositiveInfinity); + ct.SetCoefficient(x1, 3); + ct.SetCoefficient(x2, 2); + + int resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.OPTIMAL) + { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + objective.Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + Console.WriteLine("Problem solved in " + solver.Nodes() + + " branch-and-bound nodes"); + } + + private static void RunIntegerProgrammingExampleNaturalApi(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1 and x2 are integer non-negative variables. + Variable x1 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x2"); + + solver.Minimize(x1 + 2 * x2); + solver.Add(2 * x2 + 3 * x1 >= 17); + + int resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.OPTIMAL) + { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + Console.WriteLine("Problem solved in " + solver.Nodes() + + " branch-and-bound nodes"); + } + + static void Main() + { + Console.WriteLine("---- Integer programming example with GLPK ----"); + RunIntegerProgrammingExample("GLPK_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with CBC ----"); + RunIntegerProgrammingExample("CBC_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with SCIP ----"); + RunIntegerProgrammingExample("SCIP_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Integer programming example (Natural API) with GLPK ----"); + RunIntegerProgrammingExampleNaturalApi("GLPK_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with CBC ----"); + RunIntegerProgrammingExampleNaturalApi("CBC_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with SCIP ----"); + RunIntegerProgrammingExampleNaturalApi("SCIP_MIXED_INTEGER_PROGRAMMING"); + } +} diff --git a/examples/dotnet/csharp/csjobshop.cs b/examples/dotnet/csharp/csjobshop.cs new file mode 100644 index 0000000000..f1a4ed4a4a --- /dev/null +++ b/examples/dotnet/csharp/csjobshop.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +class Task { + public Task(int taskId, int jobId, int duration, int machine) { + TaskId = taskId; + JobId = jobId; + Duration = duration; + Machine = machine; + Name = "T" + taskId + "J" + jobId + "M" + + machine + "D" + duration; + } + public int TaskId {get; set;} + public int JobId {get; set;} + public int Machine {get; set;} + public int Duration {get; set;} + public string Name {get;} +} + + + +class FlexibleJobshop +{ + //Number of machines. + public const int machinesCount = 3; + //horizon is the upper bound of the start time of all tasks. + public const int horizon = 300; + //this will be set to the size of myJobList variable. + public static int jobsCount; + /*Search time limit in milliseconds. if it's equal to 0, + then no time limit will be used.*/ + public const int timeLimitInMs = 0; + public static List> myJobList = new List>(); + public static void InitTaskList() { + List taskList = new List(); + taskList.Add(new Task(0, 0, 65, 0)); + taskList.Add(new Task(1 ,0, 5, 1)); + taskList.Add(new Task(2 ,0, 15, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 1, 15, 0)); + taskList.Add(new Task(1 ,1, 25, 1)); + taskList.Add(new Task(2 ,1, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0 ,2, 25, 0)); + taskList.Add(new Task(1 ,2, 30, 1)); + taskList.Add(new Task(2 ,2, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 3, 20, 0)); + taskList.Add(new Task(1 ,3, 35, 1)); + taskList.Add(new Task(2 ,3, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 4, 15, 0)); + taskList.Add(new Task(1 ,4, 25, 1)); + taskList.Add(new Task(2 ,4, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 5, 25, 0)); + taskList.Add(new Task(1 ,5, 30, 1)); + taskList.Add(new Task(2 ,5, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 6, 20, 0)); + taskList.Add(new Task(1 ,6, 35, 1)); + taskList.Add(new Task(2 ,6, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 7, 10, 0)); + taskList.Add(new Task(1 ,7, 15, 1)); + taskList.Add(new Task(2 ,7, 50, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 8, 50, 0)); + taskList.Add(new Task(1 ,8, 10, 1)); + taskList.Add(new Task(2 ,8, 20, 2)); + myJobList.Add(taskList); + + jobsCount = myJobList.Count; + } + + public static void Main(String[] args) + { + InitTaskList(); + Solver solver = new Solver("Jobshop"); + + // ----- Creates all Intervals and vars ----- + + // All tasks + List allTasks = new List(); + // Stores all tasks attached interval variables per job. + List> + jobsToTasks = new List>(jobsCount); + // machinesToTasks stores the same interval variables as above, but + // grouped my machines instead of grouped by jobs. + List> + machinesToTasks = new List>(machinesCount); + for (int i=0; i()); + } + + // Creates all individual interval variables. + foreach (List job in myJobList) { + jobsToTasks.Add(new List()); + foreach (Task task in job) { + IntervalVar oneTask = solver.MakeFixedDurationIntervalVar( + 0, horizon, task.Duration, false, task.Name); + jobsToTasks[task.JobId].Add(oneTask); + allTasks.Add(oneTask); + machinesToTasks[task.Machine].Add(oneTask); + } + } + + // ----- Creates model ----- + + // Creates precedences inside jobs. + foreach (List jobToTask in jobsToTasks) { + int tasksCount = jobToTask.Count; + for (int task_index = 0; task_index < tasksCount - 1; ++task_index) { + IntervalVar t1 = jobToTask[task_index]; + IntervalVar t2 = jobToTask[task_index + 1]; + Constraint prec = + solver.MakeIntervalVarRelation(t2, Solver.STARTS_AFTER_END, t1); + solver.Add(prec); + } + } + + // Adds disjunctive constraints on unary resources, and creates + // sequence variables. A sequence variable is a dedicated variable + // whose job is to sequence interval variables. + SequenceVar[] allSequences = new SequenceVar[machinesCount]; + for (int machineId = 0; machineId < machinesCount; ++machineId) { + string name = "Machine_" + machineId; + DisjunctiveConstraint ct = + solver.MakeDisjunctiveConstraint(machinesToTasks[machineId].ToArray(), + name); + solver.Add(ct); + allSequences[machineId] = ct.SequenceVar(); + } + // Creates array of end_times of jobs. + IntVar[] allEnds = new IntVar[jobsCount]; + for (int i=0; i 0) { + limit = solver.MakeTimeLimit(timeLimitInMs); + } + + + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.Add(allSequences); + collector.Add(allTasks.ToArray()); + // Search. + bool solutionFound = solver.Solve(mainPhase, searchLog, objectiveMonitor, + limit, collector); + if(solutionFound) { + //The index of the solution from the collector + const int SOLUTION_INDEX = 0; + Assignment solution = collector.Solution(SOLUTION_INDEX); + for (int m = 0; m < machinesCount; ++m) { + Console.WriteLine("Machine " + m + " :"); + SequenceVar seq = allSequences[m]; + int[] storedSequence = collector.ForwardSequence(SOLUTION_INDEX, seq); + foreach (int taskIndex in storedSequence) { + IntervalVar task = seq.Interval(taskIndex); + long startMin = solution.StartMin(task); + long startMax = solution.StartMax(task); + if(startMin == startMax) { + Console.WriteLine("Task " + task.Name() + " starts at " + + startMin + "."); + } + else { + Console.WriteLine("Task " + task.Name() + " starts between " + + startMin + " and " + startMax + "."); + } + + } + } + } + else { + Console.WriteLine("No solution found!"); + } + } +} diff --git a/examples/dotnet/csharp/csknapsack.cs b/examples/dotnet/csharp/csknapsack.cs new file mode 100644 index 0000000000..a780a710e3 --- /dev/null +++ b/examples/dotnet/csharp/csknapsack.cs @@ -0,0 +1,49 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.Algorithms; + +public class CsKnapsack +{ + static void Main() + { + KnapsackSolver solver = new KnapsackSolver( + KnapsackSolver.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); + long[] profits = { 360, 83, 59, 130, 431, 67, 230, 52, 93, + 125, 670, 892, 600, 38, 48, 147, 78, 256, + 63, 17, 120, 164, 432, 35, 92, 110, 22, + 42, 50, 323, 514, 28, 87, 73, 78, 15, + 26, 78, 210, 36, 85, 189, 274, 43, 33, + 10, 19, 389, 276, 312 }; + + long[,] weights = { { 7, 0, 30, 22, 80, 94, 11, 81, 70, + 64, 59, 18, 0, 36, 3, 8, 15, 42, + 9, 0, 42, 47, 52, 32, 26, 48, 55, + 6, 29, 84, 2, 4, 18, 56, 7, 29, + 93, 44, 71, 3, 86, 66, 31, 65, 0, + 79, 20, 65, 52, 13 } }; + + long[] capacities = { 850 }; + + long optimalProfit = 7534; + + Console.WriteLine("Solving knapsack with " + profits.Length + + " items, and " + weights.GetLength(0) + " dimension"); + solver.Init(profits, weights, capacities); + long computedProfit = solver.Solve(); + + Console.WriteLine("Optimal Profit = " + computedProfit + ", expected = " + + optimalProfit); + } +} diff --git a/examples/dotnet/csharp/cslinearprogramming.cs b/examples/dotnet/csharp/cslinearprogramming.cs new file mode 100644 index 0000000000..0e4136dda6 --- /dev/null +++ b/examples/dotnet/csharp/cslinearprogramming.cs @@ -0,0 +1,176 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.LinearSolver; + +public class CsLinearProgramming +{ + private static void RunLinearProgrammingExample(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1, x2 and x3 are continuous non-negative variables. + Variable x1 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x2"); + Variable x3 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x3"); + + // Maximize 10 * x1 + 6 * x2 + 4 * x3. + Objective objective = solver.Objective(); + objective.SetCoefficient(x1, 10); + objective.SetCoefficient(x2, 6); + objective.SetCoefficient(x3, 4); + objective.SetMaximization(); + + // x1 + x2 + x3 <= 100. + Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 100.0); + c0.SetCoefficient(x1, 1); + c0.SetCoefficient(x2, 1); + c0.SetCoefficient(x3, 1); + + // 10 * x1 + 4 * x2 + 5 * x3 <= 600. + Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 600.0); + c1.SetCoefficient(x1, 10); + c1.SetCoefficient(x2, 4); + c1.SetCoefficient(x3, 5); + + // 2 * x1 + 2 * x2 + 6 * x3 <= 300. + Constraint c2 = solver.MakeConstraint(double.NegativeInfinity, 300.0); + c2.SetCoefficient(x1, 2); + c2.SetCoefficient(x2, 2); + c2.SetCoefficient(x3, 6); + + Console.WriteLine("Number of variables = " + solver.NumVariables()); + Console.WriteLine("Number of constraints = " + solver.NumConstraints()); + + int resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.OPTIMAL) { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + Console.WriteLine("x3 = " + x3.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + double[] activities = solver.ComputeConstraintActivities(); + + Console.WriteLine("Problem solved in " + solver.Iterations() + + " iterations"); + Console.WriteLine("x1: reduced cost = " + x1.ReducedCost()); + Console.WriteLine("x2: reduced cost = " + x2.ReducedCost()); + Console.WriteLine("x3: reduced cost = " + x3.ReducedCost()); + Console.WriteLine("c0: dual value = " + c0.DualValue()); + Console.WriteLine(" activity = " + activities[c0.Index()]); + Console.WriteLine("c1: dual value = " + c1.DualValue()); + Console.WriteLine(" activity = " + activities[c1.Index()]); + Console.WriteLine("c2: dual value = " + c2.DualValue()); + Console.WriteLine(" activity = " + activities[c2.Index()]); + } + + private static void RunLinearProgrammingExampleNaturalApi( + String solverType, bool printModel) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1, x2 and x3 are continuous non-negative variables. + Variable x1 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x2"); + Variable x3 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x3"); + + solver.Maximize(10 * x1 + 6 * x2 + 4 * x3); + Constraint c0 = solver.Add(x1 + x2 + x3 <= 100); + Constraint c1 = solver.Add(10 * x1 + x2 * 4 + 5 * x3 <= 600); + Constraint c2 = solver.Add(2 * x1 + 2 * x2 + 6 * x3 <= 300); + + Console.WriteLine("Number of variables = " + solver.NumVariables()); + Console.WriteLine("Number of constraints = " + solver.NumConstraints()); + + if (printModel) { + string model = solver.ExportModelAsLpFormat(false); + Console.WriteLine(model); + } + + int resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.OPTIMAL) { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + Console.WriteLine("x3 = " + x3.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + double[] activities = solver.ComputeConstraintActivities(); + Console.WriteLine("Problem solved in " + solver.Iterations() + + " iterations"); + Console.WriteLine("x1: reduced cost = " + x1.ReducedCost()); + Console.WriteLine("x2: reduced cost = " + x2.ReducedCost()); + Console.WriteLine("x3: reduced cost = " + x3.ReducedCost()); + Console.WriteLine("c0: dual value = " + c0.DualValue()); + Console.WriteLine(" activity = " + activities[c0.Index()]); + Console.WriteLine("c1: dual value = " + c1.DualValue()); + Console.WriteLine(" activity = " + activities[c1.Index()]); + Console.WriteLine("c2: dual value = " + c2.DualValue()); + Console.WriteLine(" activity = " + activities[c2.Index()]); + } + + static void Main() + { + Console.WriteLine("---- Linear programming example with GLOP ----"); + RunLinearProgrammingExample("GLOP_LINEAR_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with GLPK ----"); + RunLinearProgrammingExample("GLPK_LINEAR_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with CLP ----"); + RunLinearProgrammingExample("CLP_LINEAR_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with GLOP ----"); + RunLinearProgrammingExampleNaturalApi("GLOP_LINEAR_PROGRAMMING", true); + Console.WriteLine( + "---- Linear programming example (Natural API) with GLPK ----"); + RunLinearProgrammingExampleNaturalApi("GLPK_LINEAR_PROGRAMMING", false); + Console.WriteLine( + "---- Linear programming example (Natural API) with CLP ----"); + RunLinearProgrammingExampleNaturalApi("CLP_LINEAR_PROGRAMMING", false); + } +} diff --git a/examples/dotnet/csharp/csls_api.cs b/examples/dotnet/csharp/csls_api.cs new file mode 100644 index 0000000000..8bd6fb45e8 --- /dev/null +++ b/examples/dotnet/csharp/csls_api.cs @@ -0,0 +1,192 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +/** + * Shows how to write a custom lns operator. + */ + +public class OneVarLns : BaseLns +{ + public OneVarLns(IntVar[] vars) : base(vars) {} + + public override void InitFragments() + { + index_ = 0; + } + + public override bool NextFragment() + { + int size = Size(); + if (index_ < size) + { + AppendToFragment(index_); + ++index_; + return true; + } + else + { + return false; + } + } + + private int index_; +} + +class MoveOneVar : IntVarLocalSearchOperator { + public MoveOneVar(IntVar[] variables) : base(variables) + { + variable_index_ = 0; + move_up_ = false; + } + + protected override bool MakeOneNeighbor() + { + long current_value = OldValue(variable_index_); + if (move_up_) + { + SetValue(variable_index_, current_value + 1); + variable_index_ = (variable_index_ + 1) % Size(); + } + else + { + SetValue(variable_index_, current_value - 1); + } + move_up_ = !move_up_; + return true; + } + + // Index of the next variable to try to restore + private long variable_index_; + // Direction of the modification. + private bool move_up_; +}; + +public class SumFilter : IntVarLocalSearchFilter { + public SumFilter(IntVar[] vars) : base(vars) + { + sum_ = 0; + } + + protected override void OnSynchronize(Assignment delta) + { + sum_ = 0; + for (int index = 0; index < Size(); ++index) + { + sum_ += Value(index); + } + } + + public override bool Accept(Assignment delta, Assignment unused_deltadelta) { + AssignmentIntContainer solution_delta = delta.IntVarContainer(); + int solution_delta_size = solution_delta.Size(); + + for (int i = 0; i < solution_delta_size; ++i) + { + if (!solution_delta.Element(i).Activated()) + { + return true; + } + } + long new_sum = sum_; + for (int index = 0; index < solution_delta_size; ++index) + { + int touched_var = Index(solution_delta.Element(index).Var()); + long old_value = Value(touched_var); + long new_value = solution_delta.Element(index).Value(); + new_sum += new_value - old_value; + } + return new_sum < sum_; + } + + private long sum_; +}; + +public class CsLsApi +{ + private static void BasicLns() + { + Console.WriteLine("BasicLns"); + Solver solver = new Solver("BasicLns"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + OneVarLns one_var_lns = new OneVarLns(vars); + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(one_var_lns, db); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + private static void BasicLs() + { + Console.WriteLine("BasicLs"); + Solver solver = new Solver("BasicLs"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + MoveOneVar move_one_var = new MoveOneVar(vars); + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(move_one_var, db); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + private static void BasicLsWithFilter() + { + Console.WriteLine("BasicLsWithFilter"); + Solver solver = new Solver("BasicLs"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + MoveOneVar move_one_var = new MoveOneVar(vars); + SumFilter filter = new SumFilter(vars); + IntVarLocalSearchFilter[] filters = + new IntVarLocalSearchFilter[] { filter }; + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(move_one_var, db, null, filters); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + + public static void Main(String[] args) + { + BasicLns(); + BasicLs(); + BasicLsWithFilter(); + } +} diff --git a/examples/dotnet/csharp/csrabbitspheasants.cs b/examples/dotnet/csharp/csrabbitspheasants.cs new file mode 100644 index 0000000000..c3df0d750a --- /dev/null +++ b/examples/dotnet/csharp/csrabbitspheasants.cs @@ -0,0 +1,73 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +/** + * Shows how to write a custom decision builder. + */ +public class AssignFirstUnboundToMin : NetDecisionBuilder +{ + public AssignFirstUnboundToMin(IntVar[] vars) + { + vars_ = vars; + } + + public override Decision Next(Solver solver) + { + foreach (IntVar var in vars_) + { + if (!var.Bound()) + { + return solver.MakeAssignVariableValue(var, var.Min()); + } + } + return null; + } + + private IntVar[] vars_; +} + + +public class CsRabbitsPheasants +{ + /** + * Solves the rabbits + pheasants problem. We are seing 20 heads + * and 56 legs. How many rabbits and how many pheasants are we thus + * seeing? + */ + private static void Solve() + { + Solver solver = new Solver("RabbitsPheasants"); + IntVar rabbits = solver.MakeIntVar(0, 100, "rabbits"); + IntVar pheasants = solver.MakeIntVar(0, 100, "pheasants"); + solver.Add(rabbits + pheasants == 20); + solver.Add(rabbits * 4 + pheasants * 2 == 56); + DecisionBuilder db = + new AssignFirstUnboundToMin(new IntVar[] {rabbits, pheasants}); + solver.NewSearch(db); + solver.NextSolution(); + Console.WriteLine( + "Solved Rabbits + Pheasants in {0} ms, and {1} search tree branches.", + solver.WallTime(), solver.Branches()); + Console.WriteLine(rabbits.ToString()); + Console.WriteLine(pheasants.ToString()); + solver.EndSearch(); + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/cstsp.cs b/examples/dotnet/csharp/cstsp.cs new file mode 100644 index 0000000000..a4372d6145 --- /dev/null +++ b/examples/dotnet/csharp/cstsp.cs @@ -0,0 +1,120 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +class Tsp +{ + class RandomManhattan : NodeEvaluator2 { + public RandomManhattan(int size, int seed) + { + this.xs_ = new int[size]; + this.ys_ = new int[size]; + Random generator = new Random(seed); + for (int i = 0; i < size; ++i) + { + xs_[i] = generator.Next(1000); + ys_[i] = generator.Next(1000); + } + } + + public override long Run(int first_index, int second_index) { + return Math.Abs(xs_[first_index] - xs_[second_index]) + + Math.Abs(ys_[first_index] - ys_[second_index]); + } + + private int[] xs_; + private int[] ys_; + }; + + class ConstantCallback : NodeEvaluator2 { + public override long Run(int first_index, int second_index) { + return 1; + } + }; + + static void Solve(int size, int forbidden, int seed) + { + RoutingModel routing = new RoutingModel(size, 1, 0); + + // Setting the cost function. + // Put a permanent callback to the distance accessor here. The callback + // has the following signature: ResultCallback2. + // The two arguments are the from and to node inidices. + RandomManhattan distances = new RandomManhattan(size, seed); + routing.SetCost(distances); + + // Forbid node connections (randomly). + Random randomizer = new Random(); + long forbidden_connections = 0; + while (forbidden_connections < forbidden) { + long from = randomizer.Next(size - 1); + long to = randomizer.Next(size - 1) + 1; + if (routing.NextVar(from).Contains(to)) { + Console.WriteLine("Forbidding connection {0} -> {1}", from, to); + routing.NextVar(from).RemoveValue(to); + ++forbidden_connections; + } + } + + // Add dummy dimension to test API. + routing.AddDimension(new ConstantCallback(), + size + 1, + size + 1, + true, + "dummy"); + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters search_parameters = + RoutingModel.DefaultSearchParameters(); + // Setting first solution heuristic (cheapest addition). + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + + Assignment solution = routing.SolveWithParameters(search_parameters); + Console.WriteLine("Status = {0}", routing.Status()); + if (solution != null) { + // Solution cost. + Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); + // Inspect solution. + // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 + int route_number = 0; + for (long node = routing.Start(route_number); + !routing.IsEnd(node); + node = solution.Value(routing.NextVar(node))) { + Console.Write("{0} -> ", node); + } + Console.WriteLine("0"); + } + } + + public static void Main(String[] args) + { + int size = 10; + if (args.Length > 0) { + size = Convert.ToInt32(args[0]); + } + int forbidden = 0; + if (args.Length > 0) { + forbidden = Convert.ToInt32(args[1]); + } + int seed = 0; + if (args.Length > 2) { + seed = Convert.ToInt32(args[2]); + } + + Solve(size, forbidden, seed); + } +} diff --git a/examples/dotnet/csharp/curious_set_of_integers.cs b/examples/dotnet/csharp/curious_set_of_integers.cs new file mode 100644 index 0000000000..6e223c3baa --- /dev/null +++ b/examples/dotnet/csharp/curious_set_of_integers.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class CuriousSetOfIntegers +{ + + + public static void Decreasing(Solver solver, IntVar[] x) { + for(int i = 0; i < x.Length - 1; i++) { + solver.Add(x[i] <= x[i+1]); + } + } + + + /** + * + * Crypto problem in Google CP Solver. + * + * Martin Gardner (February 1967): + * """ + * The integers 1,3,8, and 120 form a set with a remarkable property: the + * product of any two integers is one less than a perfect square. Find + * a fifth number that can be added to the set without destroying + * this property. + * """ + * + * Also see, http://www.hakank.org/or-tools/curious_set_of_integers.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("CuriousSetOfIntegers"); + + // + // data + // + int n = 5; + int max_val = 10000; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, max_val, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + for(int i = 0; i < n - 1; i++) { + for(int j = i + 1; j < n; j++) { + IntVar p = solver.MakeIntVar(0, max_val); + solver.Add((p.Square() - 1) - (x[i] * x[j]) == 0); + } + } + + // Symmetry breaking + Decreasing(solver, x); + + // This is the original problem + // Which is the fifth number? + int[] v = {1,3,8,120}; + IntVar[] b = (from i in Enumerable.Range(0, n) + select x[i].IsMember(v)).ToArray(); + solver.Add(b.Sum() == 4); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + Solve(); + + } +} diff --git a/examples/dotnet/csharp/debruijn.cs b/examples/dotnet/csharp/debruijn.cs new file mode 100644 index 0000000000..adc232e3b3 --- /dev/null +++ b/examples/dotnet/csharp/debruijn.cs @@ -0,0 +1,202 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class DeBruijn +{ + + + /** + * + * ToNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + + /** + * + * Implements "arbitrary" de Bruijn sequences. + * See http://www.hakank.org/or-tools/debruijn_binary.py + * + */ + private static void Solve(int bbase, int n, int m) + { + Solver solver = new Solver("DeBruijn"); + + + // Ensure that the number of each digit in bin_code is + // the same. Nice feature, but it can slow things down... + bool check_same_gcc = false; // true; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(m, 0, (int)Math.Pow(bbase, n) - 1, "x"); + IntVar[,] binary = solver.MakeIntVarMatrix(m, n, 0, bbase - 1, "binary"); + + // this is the de Bruijn sequence + IntVar[] bin_code = + solver.MakeIntVarArray(m, 0, bbase - 1, "bin_code"); + + // occurences of each number in bin_code + IntVar[] gcc = solver.MakeIntVarArray(bbase, 0, m, "gcc"); + + // for the branching + IntVar[] all = new IntVar[2 * m + bbase]; + for(int i = 0; i < m; i++) { + all[i] = x[i]; + all[m + i] = bin_code[i]; + } + for(int i = 0; i < bbase; i++) { + all[2 * m + i] = gcc[i]; + } + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + // converts x <-> binary + for(int i = 0; i < m; i++) { + IntVar[] t = new IntVar[n]; + for(int j = 0; j < n; j++) { + t[j] = binary[i,j]; + } + solver.Add(ToNum(t, x[i], bbase)); + } + + // the de Bruijn condition: + // the first elements in binary[i] is the same as the last + // elements in binary[i-1] + for(int i = 1; i < m; i++) { + for(int j = 1; j < n; j++) { + solver.Add(binary[i - 1,j] == binary[i,j - 1]); + } + } + + // ... and around the corner + for(int j = 1; j < n; j++) { + solver.Add(binary[m - 1,j] == binary[0,j - 1]); + } + + // converts binary -> bin_code (de Bruijn sequence) + for(int i = 0; i < m; i++) { + solver.Add(bin_code[i] == binary[i,0]); + + } + + + // extra: ensure that all the numbers in the de Bruijn sequence + // (bin_code) has the same occurrences (if check_same_gcc is True + // and mathematically possible) + solver.Add(bin_code.Distribute(gcc)); + if (check_same_gcc && m % bbase == 0) { + for(int i = 1; i < bbase; i++) { + solver.Add(gcc[i] == gcc[i - 1]); + } + } + + // symmetry breaking: + // the minimum value of x should be first + // solver.Add(x[0] == x.Min()); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < m; i++) { + Console.Write(x[i].Value() + " "); + } + + Console.Write("\nde Bruijn sequence:"); + for(int i = 0; i < m; i++) { + Console.Write(bin_code[i].Value() + " "); + } + + Console.Write("\ngcc: "); + for(int i = 0; i < bbase; i++) { + Console.Write(gcc[i].Value() + " "); + } + Console.WriteLine("\n"); + + + // for debugging etc: show the full binary table + /* + Console.Write("binary:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + Console.Write(binary[i][j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + */ + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int bbase = 2; + int n = 3; + int m = 8; + + if (args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + m = Convert.ToInt32(args[2]); + } + + Solve(bbase, n, m); + } +} diff --git a/examples/dotnet/csharp/diet.cs b/examples/dotnet/csharp/diet.cs new file mode 100644 index 0000000000..f8d993ff25 --- /dev/null +++ b/examples/dotnet/csharp/diet.cs @@ -0,0 +1,101 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Diet +{ + /** + * + * Solves the Diet problem + * + * See http://www.hakank.org/google_or_tools/diet1.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Diet"); + + int n = 4; + int[] price = { 50, 20, 30, 80}; // in cents + + // requirements for each nutrition type + int[] limits = {500, 6, 10, 8}; + string[] products = {"A", "B", "C", "D"}; + + // nutritions for each product + int[] calories = {400, 200, 150, 500}; + int[] chocolate = {3, 2, 0, 0}; + int[] sugar = {2, 2, 4, 4}; + int[] fat = {2, 4, 1, 5}; + + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 100, "x"); + IntVar cost = x.ScalProd(price).Var(); + + + + // + // Constraints + // + + // solver.Add(solver.MakeScalProdGreaterOrEqual(x, calories, limits[0])); + solver.Add(x.ScalProd(calories) >= limits[0]); + solver.Add(x.ScalProd(chocolate) >= limits[1]); + solver.Add(x.ScalProd(sugar) >= limits[2]); + solver.Add(x.ScalProd(fat) >= limits[3]); + + // + // Objective + // + OptimizeVar obj = cost.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("cost: {0}", cost.Value()); + Console.WriteLine("Products: "); + for(int i = 0; i < n; i++) { + Console.WriteLine("{0}: {1}", products[i], x[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/discrete_tomography.cs b/examples/dotnet/csharp/discrete_tomography.cs new file mode 100644 index 0000000000..713a4639cf --- /dev/null +++ b/examples/dotnet/csharp/discrete_tomography.cs @@ -0,0 +1,216 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DiscreteTomography +{ + + // default problem + static int[] default_rowsums = {0,0,8,2,6,4,5,3,7,0,0}; + static int[] default_colsums = {0,0,7,1,6,3,4,5,2,7,0,0}; + + static int[] rowsums2; + static int[] colsums2; + + + /** + * + * Discrete tomography + * + * Problem from http://eclipse.crosscoreop.com/examples/tomo.ecl.txt + * """ + * This is a little 'tomography' problem, taken from an old issue + * of Scientific American. + * + * A matrix which contains zeroes and ones gets "x-rayed" vertically and + * horizontally, giving the total number of ones in each row and column. + * The problem is to reconstruct the contents of the matrix from this + * information. Sample run: + * + * ?- go. + * 0 0 7 1 6 3 4 5 2 7 0 0 + * 0 + * 0 + * 8 * * * * * * * * + * 2 * * + * 6 * * * * * * + * 4 * * * * + * 5 * * * * * + * 3 * * * + * 7 * * * * * * * + * 0 + * 0 + * + * Eclipse solution by Joachim Schimpf, IC-Parc + * """ + * + * See http://www.hakank.org/or-tools/discrete_tomography.py + * + */ + private static void Solve(int[] rowsums, int[] colsums) + { + + Solver solver = new Solver("DiscreteTomography"); + + // + // Data + // + int r = rowsums.Length; + int c = colsums.Length; + + Console.Write("rowsums: "); + for(int i = 0; i < r; i++) { + Console.Write(rowsums[i] + " "); + } + Console.Write("\ncolsums: "); + for(int j = 0; j < c; j++) { + Console.Write(colsums[j] + " "); + } + Console.WriteLine("\n"); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(r, c, 0, 1, "x"); + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + + // row sums + for(int i = 0; i < r; i++) { + var tmp = from j in Enumerable.Range(0, c) select x[i,j]; + solver.Add(tmp.ToArray().Sum() == rowsums[i]); + } + + // cols sums + for(int j = 0; j < c; j++) { + var tmp = from i in Enumerable.Range(0, r) select x[i,j]; + solver.Add(tmp.ToArray().Sum() == colsums[j]); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + Console.Write("{0} ", x[i,j].Value() == 1 ? "#" : "." ); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a discrete tomography file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * rowsums separated by [,\s] + * colsums separated by [,\s] + * + * e.g. + * """ + * 0,0,8,2,6,4,5,3,7,0,0 + * 0,0,7,1,6,3,4,5,2,7,0,0 + * # comment + * % another comment + * """ + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + String str; + int lineCount = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + if (lineCount == 0) { + rowsums2 = ConvLine(str); + } else if (lineCount == 1) { + colsums2 = ConvLine(str); + break; + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + private static int[] ConvLine(String str) { + String[] tmp = Regex.Split(str, "[,\\s]+"); + int len = tmp.Length; + int[] sums = new int[len]; + for(int i = 0; i < len; i++) { + sums[i] = Convert.ToInt32(tmp[i]); + } + + return sums; + + } + + public static void Main(String[] args) + { + + if(args.Length > 0) { + readFile(args[0]); + Solve(rowsums2, colsums2); + } else { + Solve(default_rowsums, default_colsums); + } + + } +} diff --git a/examples/dotnet/csharp/divisible_by_9_through_1.cs b/examples/dotnet/csharp/divisible_by_9_through_1.cs new file mode 100644 index 0000000000..ffa00fe6c2 --- /dev/null +++ b/examples/dotnet/csharp/divisible_by_9_through_1.cs @@ -0,0 +1,192 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DivisibleBy9Through1 +{ + + + /** + * + * A simple propagator for modulo constraint. + * + * This implementation is based on the ECLiPSe version + * mentioned in "A Modulo propagator for ECLiPSE" + * http://www.hakank.org/constraint_programming_blog/2010/05/a_modulo_propagator_for_eclips.html + * The ECLiPSe Prolog source code: + * http://www.hakank.org/eclipse/modulo_propagator.ecl + * + */ + public static void MyMod(Solver solver, IntVar x, IntVar y, IntVar r) { + + long lbx = x.Min(); + long ubx = x.Max(); + long ubx_neg = -ubx; + long lbx_neg = -lbx; + int min_x = (int)Math.Min(lbx, ubx_neg); + int max_x = (int)Math.Max(ubx, lbx_neg); + + IntVar d = solver.MakeIntVar(min_x, max_x, "d"); + + // r >= 0 + solver.Add(r >= 0); + + // x*r >= 0 + solver.Add( x*r >= 0); + + // -abs(y) < r + solver.Add(-y.Abs() < r); + + // r < abs(y) + solver.Add(r < y.Abs()); + + // min_x <= d, i.e. d > min_x + solver.Add(d > min_x); + + // d <= max_x + solver.Add(d <= max_x); + + // x == y*d+r + solver.Add(x - (y*d + r) == 0); + + } + + + /** + * + * ToNum(solver, a, num, base) + * + * channelling between the array a and the number num + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + /** + * + * Solves the divisible by 9 through 1 problem. + * See http://www.hakank.org/google_or_tools/divisible_by_9_through_1.py + * + */ + private static void Solve(int bbase) + { + + Solver solver = new Solver("DivisibleBy9Through1"); + + + int m = (int)Math.Pow(bbase,(bbase-1)) - 1; + int n = bbase - 1; + + String[] digits_str = {"_","0","1","2","3","4","5","6","7","8","9"}; + + Console.WriteLine("base: " + bbase); + + // + // Decision variables + // + // digits + IntVar[] x = solver.MakeIntVarArray(n, 1, bbase - 1, "x"); + + // the numbers. t[0] contains the answe + IntVar[] t = solver.MakeIntVarArray(n, 0, m, "t"); + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + // Ensure the divisibility of base .. 1 + IntVar zero = solver.MakeIntConst(0); + for(int i = 0; i < n; i++) { + int mm = bbase - i - 1; + IntVar[] tt = new IntVar[mm]; + for(int j = 0; j < mm; j++) { + tt[j] = x[j]; + } + solver.Add(ToNum(tt, t[i], bbase)); + MyMod(solver, t[i], solver.MakeIntConst(mm), zero); + + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\nt: "); + for(int i = 0; i < n; i++) { + Console.Write(t[i].Value() + " "); + } + Console.WriteLine("\n"); + + if (bbase != 10) { + Console.Write("Number base 10: " + t[0].Value()); + Console.Write(" Base " + bbase + ": "); + for(int i = 0; i < n; i++) { + Console.Write(digits_str[(int)x[i].Value() + 1]); + } + Console.WriteLine("\n"); + + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int bbase = 10; + if (args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + if (bbase > 12) { + // Though base = 12 has no solution... + Console.WriteLine("Sorry, max relevant base is 12. Setting base to 12."); + bbase = 10; + } + } + + Solve(bbase); + } +} diff --git a/examples/dotnet/csharp/dudeney.cs b/examples/dotnet/csharp/dudeney.cs new file mode 100644 index 0000000000..0ed1ca0d03 --- /dev/null +++ b/examples/dotnet/csharp/dudeney.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DudeneyNumbers +{ + + + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + /** + * + * Dudeney numbers + * From Pierre Schaus blog post + * Dudeney number + * http://cp-is-fun.blogspot.com/2010/09/test-python.html + * """ + * I discovered yesterday Dudeney Numbers + * A Dudeney Numbers is a positive integer that is a perfect cube such that the sum + * of its decimal digits is equal to the cube root of the number. There are only six + * Dudeney Numbers and those are very easy to find with CP. + * I made my first experience with google cp solver so find these numbers (model below) + * and must say that I found it very convenient to build CP models in python! + * When you take a close look at the line: + * solver.Add(sum([10**(n-i-1)*x[i] for i in range(n)]) == nb) + * It is difficult to argue that it is very far from dedicated + * optimization languages! + * """ + * + * Also see: http://en.wikipedia.org/wiki/Dudeney_number + * + */ + private static void Solve() + { + + Solver solver = new Solver("DudeneyNumbers"); + + // + // data + // + int n = 6; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 9, "x"); + IntVar nb = solver.MakeIntVar(3, (int)Math.Pow(10,n), "nb"); + IntVar s = solver.MakeIntVar(1,9*n+1,"s"); + + // + // Constraints + // + solver.Add(nb == s*s*s); + solver.Add(x.Sum() == s); + + // solver.Add(ToNum(x, nb, 10)); + + // alternative + solver.Add((from i in Enumerable.Range(0, n) + select (x[i]*(int)Math.Pow(10,n-i-1)).Var()). + ToArray().Sum() == nb); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine(nb.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + Solve(); + + } +} diff --git a/examples/dotnet/csharp/einav_puzzle2.cs b/examples/dotnet/csharp/einav_puzzle2.cs new file mode 100644 index 0000000000..7390755e8b --- /dev/null +++ b/examples/dotnet/csharp/einav_puzzle2.cs @@ -0,0 +1,228 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class EinavPuzzle2 +{ + /** + * + * A programming puzzle from Einav. + * + * From + * "A programming puzzle from Einav" + * http://gcanyon.wordpress.com/2009/10/28/a-programming-puzzle-from-einav/ + * """ + * My friend Einav gave me this programming puzzle to work on. Given + * this array of positive and negative numbers: + * 33 30 -10 -6 18 7 -11 -23 6 + * ... + * -25 4 16 30 33 -23 -4 4 -23 + * + * You can flip the sign of entire rows and columns, as many of them + * as you like. The goal is to make all the rows and columns sum to positive + * numbers (or zero), and then to find the solution (there are more than one) + * that has the smallest overall sum. So for example, for this array: + * 33 30 -10 + * -16 19 9 + * -17 -12 -14 + * You could flip the sign for the bottom row to get this array: + * 33 30 -10 + * -16 19 9 + * 17 12 14 + * Now all the rows and columns have positive sums, and the overall total is + * 108. + * But you could instead flip the second and third columns, and the second + * row, to get this array: + * 33 -30 10 + * 16 19 9 + * -17 12 14 + * All the rows and columns still total positive, and the overall sum is just + * 66. So this solution is better (I don't know if it's the best) + * A pure brute force solution would have to try over 30 billion solutions. + * I wrote code to solve this in J. I'll post that separately. + * """ + * + * Note: + * This is a port of Larent Perrons's Python version of my own einav_puzzle.py. + * He removed some of the decision variables and made it more efficient. + * Thanks! + * + * Also see http://www.hakank.org/or-tools/einav_puzzle2.py + * + */ + private static void Solve() + { + Solver solver = new Solver("EinavPuzzle2"); + + // + // Data + // + + // Small problem + // int rows = 3; + // int cols = 3; + // int[,] data = { + // { 33, 30, -10}, + // {-16, 19, 9}, + // {-17, -12, -14} + // }; + + + // Full problem + int rows = 27; + int cols = 9; + int[,] data = { + {33,30,10,-6,18,-7,-11,23,-6}, + {16,-19,9,-26,-8,-19,-8,-21,-14}, + {17,12,-14,31,-30,13,-13,19,16}, + {-6,-11,1,17,-12,-4,-7,14,-21}, + {18,-31,34,-22,17,-19,20,24,6}, + {33,-18,17,-15,31,-5,3,27,-3}, + {-18,-20,-18,31,6,4,-2,-12,24}, + {27,14,4,-29,-3,5,-29,8,-12}, + {-15,-7,-23,23,-9,-8,6,8,-12}, + {33,-23,-19,-4,-8,-7,11,-12,31}, + {-20,19,-15,-30,11,32,7,14,-5}, + {-23,18,-32,-2,-31,-7,8,24,16}, + {32,-4,-10,-14,-6,-1,0,23,23}, + {25,0,-23,22,12,28,-27,15,4}, + {-30,-13,-16,-3,-3,-32,-3,27,-31}, + {22,1,26,4,-2,-13,26,17,14}, + {-9,-18,3,-20,-27,-32,-11,27,13}, + {-17,33,-7,19,-32,13,-31,-2,-24}, + {-31,27,-31,-29,15,2,29,-15,33}, + {-18,-23,15,28,0,30,-4,12,-32}, + {-3,34,27,-25,-18,26,1,34,26}, + {-21,-31,-10,-13,-30,-17,-12,-26,31}, + {23,-31,-19,21,-17,-10,2,-23,23}, + {-3,6,0,-3,-32,0,-10,-25,14}, + {-19,9,14,-27,20,15,-5,-27,18}, + {11,-6,24,7,-17,26,20,-31,-25}, + {-25,4,-16,30,33,23,-4,-4,23} + }; + + + IEnumerable ROWS = Enumerable.Range(0, rows); + IEnumerable COLS = Enumerable.Range(0, cols); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(rows, cols, -100, 100, "x"); + IntVar[] x_flat = x.Flatten(); + + int[] signs_domain = {-1,1}; + // This don't work at the moment... + IntVar[] row_signs = solver.MakeIntVarArray(rows, signs_domain, "row_signs"); + IntVar[] col_signs = solver.MakeIntVarArray(cols, signs_domain, "col_signs"); + + + + // To optimize + IntVar total_sum = x_flat.Sum().VarWithName("total_sum"); + + // + // Constraints + // + foreach(int i in ROWS) { + foreach(int j in COLS) { + solver.Add(x[i,j] == data[i,j] * row_signs[i] * col_signs[j]); + } + } + + // row sums + IntVar[] row_sums = (from i in ROWS + select (from j in COLS + select x[i,j] + ).ToArray().Sum().Var()).ToArray(); + + foreach(int i in ROWS) { + row_sums[i].SetMin(0); + } + + // col sums + IntVar[] col_sums = (from j in COLS + select (from i in ROWS + select x[i,j] + ).ToArray().Sum().Var()).ToArray(); + + foreach(int j in COLS) { + col_sums[j].SetMin(0); + } + + + // + // Objective + // + OptimizeVar obj = total_sum.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(col_signs.Concat(row_signs).ToArray(), + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("Sum: {0}",total_sum.Value()); + Console.Write("row_sums: "); + foreach(int i in ROWS) { + Console.Write(row_sums[i].Value() + " "); + } + Console.Write("\nrow_signs: "); + foreach(int i in ROWS) { + Console.Write(row_signs[i].Value() + " "); + } + + Console.Write("\ncol_sums: "); + foreach(int j in COLS) { + Console.Write(col_sums[j].Value() + " "); + } + Console.Write("\ncol_signs: "); + foreach(int j in COLS) { + Console.Write(col_signs[j].Value() + " "); + } + Console.WriteLine("\n"); + foreach(int i in ROWS) { + foreach(int j in COLS) { + Console.Write("{0,3} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/eq10.cs b/examples/dotnet/csharp/eq10.cs new file mode 100644 index 0000000000..e91bb60492 --- /dev/null +++ b/examples/dotnet/csharp/eq10.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Eq10 +{ + /** + * + * Eq 10 in Google CP Solver. + * + * Standard benchmark problem. + * + * Also see http://hakank.org/or-tools/eq10.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Eq10"); + + int n = 7; + + // + // Decision variables + // + IntVar X1 = solver.MakeIntVar(0, 10, "X1"); + IntVar X2 = solver.MakeIntVar(0, 10, "X2"); + IntVar X3 = solver.MakeIntVar(0, 10, "X3"); + IntVar X4 = solver.MakeIntVar(0, 10, "X4"); + IntVar X5 = solver.MakeIntVar(0, 10, "X5"); + IntVar X6 = solver.MakeIntVar(0, 10, "X6"); + IntVar X7 = solver.MakeIntVar(0, 10, "X7"); + + IntVar[] X = {X1,X2,X3,X4,X5,X6,X7}; + + + // + // Constraints + // + solver.Add(0+98527*X1+34588*X2+5872*X3+59422*X5+65159*X7 + == 1547604+30704*X4+29649*X6); + + solver.Add(0+98957*X2+83634*X3+69966*X4+62038*X5+37164*X6+85413*X7 + == 1823553+93989*X1); + + solver.Add(900032+10949*X1+77761*X2+67052*X5 + == 0+80197*X3+61944*X4+92964*X6+44550*X7); + + solver.Add(0+73947*X1+84391*X3+81310*X5 + == 1164380+96253*X2+44247*X4+70582*X6+33054*X7); + + solver.Add(0+13057*X3+42253*X4+77527*X5+96552*X7 + == 1185471+60152*X1+21103*X2+97932*X6); + + solver.Add(1394152+66920*X1+55679*X4 + == 0+64234*X2+65337*X3+45581*X5+67707*X6+98038*X7); + + solver.Add(0+68550*X1+27886*X2+31716*X3+73597*X4+38835*X7 + == 279091+88963*X5+76391*X6); + + solver.Add(0+76132*X2+71860*X3+22770*X4+68211*X5+78587*X6 + == 480923+48224*X1+82817*X7); + + solver.Add(519878+94198*X2+87234*X3+37498*X4 + == 0+71583*X1+25728*X5+25495*X6+70023*X7); + + solver.Add(361921+78693*X1+38592*X5+38478*X6 + == 0+94129*X2+43188*X3+82528*X4+69025*X7); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(X, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(X[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/eq20.cs b/examples/dotnet/csharp/eq20.cs new file mode 100644 index 0000000000..147768944d --- /dev/null +++ b/examples/dotnet/csharp/eq20.cs @@ -0,0 +1,124 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Eq20 +{ + /** + * + * Eq 20 in Google CP Solver. + * + * Standard benchmark problem. + * + * Also see http://hakank.org/or-tools/eq20.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Eq20"); + + int n = 7; + + // + // Decision variables + // + IntVar X0 = solver.MakeIntVar(0, 10, "X0"); + IntVar X1 = solver.MakeIntVar(0, 10, "X1"); + IntVar X2 = solver.MakeIntVar(0, 10, "X2"); + IntVar X3 = solver.MakeIntVar(0, 10, "X3"); + IntVar X4 = solver.MakeIntVar(0, 10, "X4"); + IntVar X5 = solver.MakeIntVar(0, 10, "X5"); + IntVar X6 = solver.MakeIntVar(0, 10, "X6"); + + IntVar[] X = {X0,X1,X2,X3,X4,X5,X6}; + + + // + // Constraints + // + solver.Add(-76706*X0 + 98205*X1 + 23445*X2 + 67921*X3 + 24111*X4 + + -48614*X5 + -41906*X6 == 821228); + solver.Add(87059*X0 + -29101*X1 + -5513*X2 + -21219*X3 + 22128*X4 + + 7276*X5 + 57308*X6 == 22167); + solver.Add(-60113*X0 + 29475*X1 + 34421*X2 + -76870*X3 + 62646*X4 + + 29278*X5 + -15212*X6 == 251591); + solver.Add(49149*X0 + 52871*X1 + -7132*X2 + 56728*X3 + -33576*X4 + + -49530*X5 + -62089*X6 == 146074); + solver.Add(-10343*X0 + 87758*X1 + -11782*X2 + 19346*X3 + 70072*X4 + + -36991*X5 + 44529*X6 == 740061); + solver.Add(85176*X0 + -95332*X1 + -1268*X2 + 57898*X3 + 15883*X4 + + 50547*X5 + 83287*X6 == 373854); + solver.Add(-85698*X0 + 29958*X1 + 57308*X2 + 48789*X3 + -78219*X4 + + 4657*X5 + 34539*X6 == 249912); + solver.Add(-67456*X0 + 84750*X1 + -51553*X2 + 21239*X3 + 81675*X4 + + -99395*X5 + -4254*X6 == 277271); + solver.Add(94016*X0 + -82071*X1 + 35961*X2 + 66597*X3 + -30705*X4 + + -44404*X5 + -38304*X6 == 25334); + solver.Add(-60301*X0 + 31227*X1 + 93951*X2 + 73889*X3 + 81526*X4 + + -72702*X5 + 68026*X6 == 1410723); + solver.Add(-16835*X0 + 47385*X1 + 97715*X2 + -12640*X3 + 69028*X4 + + 76212*X5 + -81102*X6 == 1244857); + solver.Add(-43277*X0 + 43525*X1 + 92298*X2 + 58630*X3 + 92590*X4 + + -9372*X5 + -60227*X6 == 1503588); + solver.Add(-64919*X0 + 80460*X1 + 90840*X2 + -59624*X3 + -75542*X4 + + 25145*X5 + -47935*X6 == 18465); + solver.Add(-45086*X0 + 51830*X1 + -4578*X2 + 96120*X3 + 21231*X4 + + 97919*X5 + 65651*X6 == 1198280); + solver.Add(85268*X0 + 54180*X1 + -18810*X2 + -48219*X3 + 6013*X4 + + 78169*X5 + -79785*X6 == 90614); + solver.Add(8874*X0 + -58412*X1 + 73947*X2 + 17147*X3 + 62335*X4 + + 16005*X5 + 8632*X6 == 752447); + solver.Add(71202*X0 + -11119*X1 + 73017*X2 + -38875*X3 + -14413*X4 + + -29234*X5 + 72370*X6 == 129768); + solver.Add(1671*X0 + -34121*X1 + 10763*X2 + 80609*X3 + 42532*X4 + + 93520*X5 + -33488*X6 == 915683); + solver.Add(51637*X0 + 67761*X1 + 95951*X2 + 3834*X3 + -96722*X4 + + 59190*X5 + 15280*X6 == 533909); + solver.Add(-16105*X0 + 62397*X1 + -6704*X2 + 43340*X3 + 95100*X4 + + -68610*X5 + 58301*X6 == 876370); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(X, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(X[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/examples.csproj b/examples/dotnet/csharp/examples.csproj new file mode 100644 index 0000000000..ccebf3f057 --- /dev/null +++ b/examples/dotnet/csharp/examples.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp2.0 + + + + full + true + true + APuzzle.Main + + + diff --git a/examples/dotnet/csharp/fill_a_pix.cs b/examples/dotnet/csharp/fill_a_pix.cs new file mode 100644 index 0000000000..83e5c65963 --- /dev/null +++ b/examples/dotnet/csharp/fill_a_pix.cs @@ -0,0 +1,248 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class FillAPix +{ + + static int X = -1; + + // + // Default problem. + // Puzzle 1 from + // http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules + + // + static int default_n = 10; + static int[,] default_puzzle = {{X,X,X,X,X,X,X,X,0,X}, + {X,8,8,X,2,X,0,X,X,X}, + {5,X,8,X,X,X,X,X,X,X}, + {X,X,X,X,X,2,X,X,X,2}, + {1,X,X,X,4,5,6,X,X,X}, + {X,0,X,X,X,7,9,X,X,6}, + {X,X,X,6,X,X,9,X,X,6}, + {X,X,6,6,8,7,8,7,X,5}, + {X,4,X,6,6,6,X,6,X,4}, + {X,X,X,X,X,X,3,X,X,X}}; + + // for the actual problem + static int n; + static int[,] puzzle; + + + /** + * + * Fill-a-Pix problem + * + * From http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/basiclogic + * """ + * Each puzzle consists of a grid containing clues in various places. The + * object is to reveal a hidden picture by painting the squares around each + * clue so that the number of painted squares, including the square with + * the clue, matches the value of the clue. + * """ + * + * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules + * """ + * Fill-a-Pix is a Minesweeper-like puzzle based on a grid with a pixilated + * picture hidden inside. Using logic alone, the solver determines which + * squares are painted and which should remain empty until the hidden picture + * is completely exposed. + * """ + * + * Fill-a-pix History: + * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/history + * + * Also see http://www.hakank.org/google_or_tools/fill_a_pix.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("FillAPix"); + + // + // data + // + int[] S = {-1, 0, 1}; + + Console.WriteLine("Problem:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (puzzle[i,j] > X) { + Console.Write(puzzle[i,j] + " "); + } else { + Console.Write("X "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] pict = solver.MakeIntVarMatrix(n, n, 0, 1, "pict"); + IntVar[] pict_flat = pict.Flatten(); // for branching + + // + // Constraints + // + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (puzzle[i,j] > X) { + + // this cell is the sum of all surrounding cells + var tmp = from a in S from b in S where + i + a >= 0 && + j + b >= 0 && + i + a < n && + j + b < n + select(pict[i+a,j+b]); + + solver.Add(tmp.ToArray().Sum() == puzzle[i,j]); + + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(pict_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write(pict[i,j].Value() == 1 ? "#" : " "); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Fill-a-Pix file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows and columns (n x n) + * < + * row number of neighbours lines... + * > + * + * 0..8 means number of neighbours, "." mean unknown (may be a mine) + * + * Example (from fill_a_pix1.txt): + * + * 10 + * ........0. + * .88.2.0... + * 5.8....... + * .....2...2 + * 1...456... + * .0...79..6 + * ...6..9..6 + * ..668787.5 + * .4.666.6.4 + * ......3... + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + n = Convert.ToInt32(str); // number of rows + puzzle = new int[n,n]; + } else { + // the problem matrix + String[] row = Regex.Split(str, ""); + for(int j = 1; j <= n; j++) { + String s = row[j]; + if (s.Equals(".")) { + puzzle[lineCount-1, j-1] = -1; + } else { + puzzle[lineCount-1, j-1] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + puzzle = default_puzzle; + n = default_n; + } + + Solve(); + } +} diff --git a/examples/dotnet/csharp/furniture_moving.cs b/examples/dotnet/csharp/furniture_moving.cs new file mode 100644 index 0000000000..2c96c43f54 --- /dev/null +++ b/examples/dotnet/csharp/furniture_moving.cs @@ -0,0 +1,180 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class FurnitureMoving +{ + + /* + * Decompositon of cumulative. + * + * Inspired by the MiniZinc implementation: + * http://www.g12.csse.unimelb.edu.au/wiki/doku.php?id=g12:zinc:lib:minizinc:std:cumulative.mzn&s[]=cumulative + * The MiniZinc decomposition is discussed in the paper: + * A. Schutt, T. Feydy, P.J. Stuckey, and M. G. Wallace. + * "Why cumulative decomposition is not as bad as it sounds." + * Download: + * http://www.cs.mu.oz.au/%7Epjs/rcpsp/papers/cp09-cu.pdf + * http://www.cs.mu.oz.au/%7Epjs/rcpsp/cumu_lazyfd.pdf + * + * + * Parameters: + * + * s: start_times assumption: IntVar[] + * d: durations assumption: int[] + * r: resources assumption: int[] + * b: resource limit assumption: IntVar or int + * + * + */ + static void MyCumulative(Solver solver, + IntVar[] s, + int[] d, + int[] r, + IntVar b) { + + int[] tasks = (from i in Enumerable.Range(0, s.Length) + where r[i] > 0 && d[i] > 0 + select i).ToArray(); + int times_min = tasks.Min(i => (int)s[i].Min()); + int d_max = d.Max(); + int times_max = tasks.Max(i => (int)s[i].Max() + d_max); + for(int t = times_min; t <= times_max; t++) { + ArrayList bb = new ArrayList(); + foreach(int i in tasks) { + bb.Add(((s[i] <= t) * (s[i] + d[i]> t) * r[i]).Var()); + } + solver.Add((bb.ToArray(typeof(IntVar)) as IntVar[]).Sum() <= b); + } + + // Somewhat experimental: + // This constraint is needed to constrain the upper limit of b. + if (b is IntVar) { + solver.Add(b <= r.Sum()); + } + + } + + + /** + * + * Moving furnitures (scheduling) problem in Google CP Solver. + * + * Marriott & Stukey: 'Programming with constraints', page 112f + * + * The model implements an decomposition of the global constraint + * cumulative (see above). + * + * Also see http://www.hakank.org/or-tools/furniture_moving.py + * + */ + private static void Solve() + { + Solver solver = new Solver("FurnitureMoving"); + + int n = 4; + int[] duration = {30,10,15,15}; + int[] demand = { 3, 1, 3, 2}; + int upper_limit = 160; + + + // + // Decision variables + // + IntVar[] start_times = solver.MakeIntVarArray(n, 0, upper_limit, "start_times"); + IntVar[] end_times = solver.MakeIntVarArray(n, 0, upper_limit * 2, "end_times"); + IntVar end_time = solver.MakeIntVar(0, upper_limit * 2, "end_time"); + + // number of needed resources, to be minimized or constrained + IntVar num_resources = solver.MakeIntVar(0, 10, "num_resources"); + + + // + // Constraints + // + for(int i = 0; i < n; i++) { + solver.Add(end_times[i] == start_times[i] + duration[i]); + } + + solver.Add(end_time == end_times.Max()); + MyCumulative(solver, start_times, duration, demand, num_resources); + + + // + // Some extra constraints to play with + // + + // all tasks must end within an hour + // solver.Add(end_time <= 60); + + // All tasks should start at time 0 + // for(int i = 0; i < n; i++) { + // solver.Add(start_times[i] == 0); + // } + + + // limitation of the number of people + // solver.Add(num_resources <= 3); + solver.Add(num_resources <= 4); + + + // + // Objective + // + + // OptimizeVar obj = num_resources.Minimize(1); + OptimizeVar obj = end_time.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(start_times, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_resources: {0} end_time: {1}", + num_resources.Value(), end_time.Value()); + for(int i = 0; i < n; i++) { + Console.WriteLine("Task {0,1}: {1,2} -> {2,2} -> {3,2} (demand: {4})", + i, + start_times[i].Value(), + duration[i], + end_times[i].Value(), + demand[i]); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/furniture_moving_intervals.cs b/examples/dotnet/csharp/furniture_moving_intervals.cs new file mode 100644 index 0000000000..24d7eba72c --- /dev/null +++ b/examples/dotnet/csharp/furniture_moving_intervals.cs @@ -0,0 +1,150 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class FurnitureMovingIntervals +{ + /** + * + * Moving furnitures (scheduling) problem in Google CP Solver. + * + * Marriott & Stukey: 'Programming with constraints', page 112f + * + * Also see http://www.hakank.org/or-tools/furniture_moving.py + * + */ + private static void Solve() + { + Solver solver = new Solver("FurnitureMovingIntervals"); + + const int n = 4; + int[] durations = {30,10,15,15}; + int[] demand = {3, 1, 3, 2}; + const int upper_limit = 160; + const int max_num_workers = 5; + + // + // Decision variables + // + IntervalVar[] tasks = new IntervalVar[n]; + for (int i = 0; i < n; ++i) + { + tasks[i] = solver.MakeFixedDurationIntervalVar(0, + upper_limit - durations[i], + durations[i], + false, + "task_" + i); + } + + // Fillers that span the whole resource and limit the available + // number of workers. + IntervalVar[] fillers = new IntervalVar[max_num_workers]; + for (int i = 0; i < max_num_workers; ++i) + { + fillers[i] = solver.MakeFixedDurationIntervalVar(0, + 0, + upper_limit, + true, + "filler_" + i); + } + + // Number of needed resources, to be minimized or constrained. + IntVar num_workers = solver.MakeIntVar(0, max_num_workers, "num_workers"); + // Links fillers and num_workers. + for (int i = 0; i < max_num_workers; ++i) + { + solver.Add((num_workers > i) + fillers[i].PerformedExpr() == 1); + } + + // Creates makespan. + IntVar[] ends = new IntVar[n]; + for (int i = 0; i < n; ++i) + { + ends[i] = tasks[i].EndExpr().Var(); + } + IntVar end_time = ends.Max().VarWithName("end_time"); + + // + // Constraints + // + IntervalVar[] all_tasks = new IntervalVar[n + max_num_workers]; + int[] all_demands = new int[n + max_num_workers]; + for (int i = 0; i < n; ++i) + { + all_tasks[i] = tasks[i]; + all_demands[i] = demand[i]; + } + for (int i = 0; i < max_num_workers; ++i) + { + all_tasks[i + n] = fillers[i]; + all_demands[i + n] = 1; + } + solver.Add(all_tasks.Cumulative(all_demands, max_num_workers, "workers")); + + // + // Some extra constraints to play with + // + + // all tasks must end within an hour + // solver.Add(end_time <= 60); + + // All tasks should start at time 0 + // for(int i = 0; i < n; i++) { + // solver.Add(tasks[i].StartAt(0)); + // } + + + // limitation of the number of people + // solver.Add(num_workers <= 3); + solver.Add(num_workers <= 4); + + // + // Objective + // + + // OptimizeVar obj = num_workers.Minimize(1); + OptimizeVar obj = end_time.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_tasks, Solver.INTERVAL_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine(num_workers.ToString() + ", " + end_time.ToString()); + for(int i = 0; i < n; i++) { + Console.WriteLine("{0} (demand:{1})", tasks[i].ToString(), demand[i]); + } + Console.WriteLine(); + } + + Console.WriteLine("Solutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/futoshiki.cs b/examples/dotnet/csharp/futoshiki.cs new file mode 100644 index 0000000000..9a9b90ebc8 --- /dev/null +++ b/examples/dotnet/csharp/futoshiki.cs @@ -0,0 +1,200 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Futoshiki +{ + + /** + * + * Futoshiki problem. + * + * From http://en.wikipedia.org/wiki/Futoshiki + * """ + * The puzzle is played on a square grid, such as 5 x 5. The objective + * is to place the numbers 1 to 5 (or whatever the dimensions are) + * such that each row, and column contains each of the digits 1 to 5. + * Some digits may be given at the start. In addition, inequality + * constraints are also initially specifed between some of the squares, + * such that one must be higher or lower than its neighbour. These + * constraints must be honoured as the grid is filled out. + * """ + * + * Also see http://www.hakank.org/or-tools/futoshiki.py + * + */ + private static void Solve(int[,] values, int[,] lt) + { + + Solver solver = new Solver("Futoshiki"); + + int size = values.GetLength(0); + IEnumerable RANGE = Enumerable.Range(0, size); + IEnumerable NUMQD = Enumerable.Range(0, lt.GetLength(0)); + + // + // Decision variables + // + IntVar[,] field = solver.MakeIntVarMatrix(size, size, 1, size, "field"); + IntVar[] field_flat = field.Flatten(); + + // + // Constraints + // + + + // set initial values + foreach(int row in RANGE) { + foreach(int col in RANGE) { + if (values[row,col] > 0) { + solver.Add(field[row,col] == values[row,col]); + } + } + } + + + // all rows have to be different + foreach(int row in RANGE) { + solver.Add((from col in RANGE + select field[row,col]).ToArray().AllDifferent()); + } + + + // all columns have to be different + foreach(int col in RANGE) { + solver.Add((from row in RANGE + select field[row,col]).ToArray().AllDifferent()); + } + + + // all < constraints are satisfied + // Also: make 0-based + foreach(int i in NUMQD) { + solver.Add(field[ lt[i,0]-1, lt[i,1]-1 ] < + field[ lt[i,2]-1, lt[i,3]-1 ] ); + } + + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(field_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int i in RANGE) { + foreach(int j in RANGE) { + Console.Write("{0} ", field[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + // + // Example from Tailor model futoshiki.param/futoshiki.param + // Solution: + // 5 1 3 2 4 + // 1 4 2 5 3 + // 2 3 1 4 5 + // 3 5 4 1 2 + // 4 2 5 3 1 + // + // Futoshiki instance, by Andras Salamon + // specify the numbers in the grid + // + int[,] values1 = { + {0, 0, 3, 2, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}}; + + + // [i1,j1, i2,j2] requires that values[i1,j1] < values[i2,j2] + // Note: 1-based + int [,] lt1 = { + {1,2, 1,1}, + {1,4, 1,5}, + {2,3, 1,3}, + {3,3, 2,3}, + {3,4, 2,4}, + {2,5, 3,5}, + {3,2, 4,2}, + {4,4, 4,3}, + {5,2, 5,1}, + {5,4, 5,3}, + {5,5, 4,5}}; + + + // + // Example from http://en.wikipedia.org/wiki/Futoshiki + // Solution: + // 5 4 3 2 1 + // 4 3 1 5 2 + // 2 1 4 3 5 + // 3 5 2 1 4 + // 1 2 5 4 3 + // + int[,] values2 = { + {0, 0, 0, 0, 0}, + {4, 0, 0, 0, 2}, + {0, 0, 4, 0, 0}, + {0, 0, 0, 0, 4}, + {0, 0, 0, 0, 0}}; + + // Note: 1-based + int[,] lt2 = { + {1,2, 1,1}, + {1,4, 1,3}, + {1,5, 1,4}, + {4,4, 4,5}, + {5,1, 5,2}, + {5,2, 5,3} + }; + + Console.WriteLine("Problem 1"); + Solve(values1, lt1); + + Console.WriteLine("\nProblem 2"); + Solve(values2, lt2); + + } +} diff --git a/examples/dotnet/csharp/gate_scheduling_sat.cs b/examples/dotnet/csharp/gate_scheduling_sat.cs new file mode 100644 index 0000000000..d9ee8a1faf --- /dev/null +++ b/examples/dotnet/csharp/gate_scheduling_sat.cs @@ -0,0 +1,154 @@ +// Copyright 2010-2017 Google +// 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. + +// Gate Scheduling problem. +// +// We have a set of jobs to perform (duration, width). +// We have two parallel machines that can perform this job. +// One machine can only perform one job at a time. +// At any point in time, the sum of the width of the two active jobs does not +// exceed a max_length. +// +//The objective is to minimize the max end time of all jobs. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class GateSchedulingSat +{ + static void Solve() + { + CpModel model = new CpModel(); + + int[,] jobs = new [,] {{3, 3}, + {2, 5}, + {1, 3}, + {3, 7}, + {7, 3}, + {2, 2}, + {2, 2}, + {5, 5}, + {10, 2}, + {4, 3}, + {2, 6}, + {1, 2}, + {6, 8}, + {4, 5}, + {3, 7}}; + + int max_length = 10; + int num_jobs = jobs.GetLength(0); + var all_jobs = Enumerable.Range(0, num_jobs); + + int horizon = 0; + foreach (int j in all_jobs) + { + horizon += jobs[j, 0]; + } + + + List intervals = new List(); + List intervals0 = new List(); + List intervals1 = new List(); + List performed = new List(); + List starts = new List(); + List ends = new List(); + List demands = new List(); + + foreach (int i in all_jobs) + { + // Create main interval. + IntVar start = model.NewIntVar(0, horizon, String.Format("start_{0}", i)); + int duration = jobs[i, 0]; + IntVar end = model.NewIntVar(0, horizon, String.Format("end_{0}", i)); + IntervalVar interval = model.NewIntervalVar( + start, duration, end, String.Format("interval_{0}", i)); + starts.Add(start); + intervals.Add(interval); + ends.Add(end); + demands.Add(jobs[i, 1]); + + IntVar performed_on_m0 = + model.NewBoolVar(String.Format("perform_{0}_on_m0", i)); + performed.Add(performed_on_m0); + + // Create an optional copy of interval to be executed on machine 0. + IntVar start0 = model.NewOptionalIntVar( + 0, horizon, performed_on_m0, String.Format("start_{0}_on_m0", i)); + IntVar end0 = model.NewOptionalIntVar( + 0, horizon, performed_on_m0, String.Format("end_{0}_on_m0", i)); + IntervalVar interval0 = model.NewOptionalIntervalVar( + start0, duration, end0, performed_on_m0, + String.Format("interval_{0}_on_m0", i)); + intervals0.Add(interval0); + + // Create an optional copy of interval to be executed on machine 1. + IntVar start1 = model.NewOptionalIntVar( + 0, horizon, performed_on_m0.Not(), + String.Format("start_{0}_on_m1", i)); + IntVar end1 = model.NewOptionalIntVar(0, horizon, performed_on_m0.Not(), + String.Format("end_{0}_on_m1", i)); + IntervalVar interval1 = model.NewOptionalIntervalVar( + start1, duration, end1, performed_on_m0.Not(), + String.Format("interval_{0}_on_m1", i)); + intervals1.Add(interval1); + + // We only propagate the constraint if the tasks is performed on the + // machine. + model.Add(start0 == start).OnlyEnforceIf(performed_on_m0); + model.Add(start1 == start).OnlyEnforceIf(performed_on_m0.Not()); + } + + // Max Length constraint (modeled as a cumulative) + model.AddCumulative(intervals, demands, max_length); + + // Choose which machine to perform the jobs on. + model.AddNoOverlap(intervals0); + model.AddNoOverlap(intervals1); + + // Objective variable. + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, ends); + model.Minimize(makespan); + + // Symmetry breaking. + model.Add(performed[0] == 0); + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + solver.Solve(model); + + + // Output solution. + Console.WriteLine("Solution"); + Console.WriteLine(" - makespan = " + solver.ObjectiveValue); + foreach (int i in all_jobs) + { + long performed_machine = 1 - solver.Value(performed[i]); + long start = solver.Value(starts[i]); + Console.WriteLine( + String.Format(" - Job {0} starts at {1} on machine {2}", + i, start, performed_machine)); + } + Console.WriteLine("Statistics"); + Console.WriteLine(" - conflicts : " + solver.NumConflicts()); + Console.WriteLine(" - branches : " + solver.NumBranches()); + Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); + } + + static void Main() { + Solve(); + } +} \ No newline at end of file diff --git a/examples/dotnet/csharp/golomb_ruler.cs b/examples/dotnet/csharp/golomb_ruler.cs new file mode 100644 index 0000000000..f7cbb9a8a9 --- /dev/null +++ b/examples/dotnet/csharp/golomb_ruler.cs @@ -0,0 +1,128 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class GolombRuler +{ + + /** + * + * Golomb Ruler problem. + * + * This C# implementation is based on Charles Prud'homme's + * or-tools/Java model: + * http://code.google.com/p/or-tools/source/browse/trunk/com/google/ortools/constraintsolver/samples/GolombRuler.java + * + */ + private static void Solve(int m = 8) + { + Solver solver = new Solver("GolombRuler"); + + + // + // Decision variables + // + IntVar[] ticks = solver.MakeIntVarArray(m, + 0, + ((m < 31) ? (1 << (m + 1)) - 1 : 9999), + "ticks"); + + IntVar[] diff = new IntVar[(m * m - m) / 2]; + + + // + // Constraints + // + solver.Add(ticks[0] == 0); + + for(int i = 0; i < ticks.Length - 1; i++) { + solver.Add(ticks[i] < ticks[i+1]); + } + + + for (int k = 0, i = 0; i < m - 1; i++) { + for (int j = i + 1; j < m; j++, k++) { + diff[k] = (ticks[j]-ticks[i]).Var(); + solver.Add(diff[k] >= (j - i) * (j - i + 1) / 2); + } + } + + solver.Add(diff.AllDifferent()); + + // break symetries + if (m > 2) { + solver.Add(diff[0] < diff[diff.Length - 1]); + } + + + // + // Optimization + // + OptimizeVar opt = ticks[m - 1].Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(ticks, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + // We just want the debug info for larger instances. + if (m >= 11) { + + SearchMonitor log = solver.MakeSearchLog(10000, opt); + solver.NewSearch(db, opt, log); + + } else { + + solver.NewSearch(db, opt); + } + + + while (solver.NextSolution()) { + Console.Write("opt: {0} [ ", ticks[m-1].Value()); + for(int i = 0; i < m; i++) { + Console.Write("{0} ", ticks[i].Value()); + } + Console.WriteLine("]"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 8; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/grocery.cs b/examples/dotnet/csharp/grocery.cs new file mode 100644 index 0000000000..75d6c1dc0a --- /dev/null +++ b/examples/dotnet/csharp/grocery.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + + +public class Grocery +{ + + public static void Decreasing(Solver solver, IntVar[] x) { + for(int i = 0; i < x.Length - 1; i++) { + solver.Add(x[i] <= x[i+1]); + } + } + + // + // Simple decomposition of Prod() for an IntVar array + // + private static Constraint MyProd(IntVar[] x, int prod) { + int len = x.Length; + IntVar[] tmp = new IntVar[len]; + tmp[0] = x[0]; + for(int i = 1; i < len; i++) { + tmp[i] = (tmp[i-1]*x[i]).Var(); + } + + return tmp[len-1] == prod; + } + + + /** + * + * Grocery problem. + * + * From Christian Schulte, Gert Smolka, Finite Domain + * http://www.mozart-oz.org/documentation/fdt/ + * Constraint Programming in Oz. A Tutorial. 2001. + * """ + * A kid goes into a grocery store and buys four items. The cashier + * charges $7.11, the kid pays and is about to leave when the cashier + * calls the kid back, and says 'Hold on, I multiplied the four items + * instead of adding them; I'll try again; Hah, with adding them the + * price still comes to $7.11'. What were the prices of the four items? + * """ + * + */ + private static void Solve() + { + Solver solver = new Solver("Grocery"); + + int n = 4; + int c = 711; + + // + // Decision variables + // + + IntVar[] item = solver.MakeIntVarArray(n, 0, c / 2, "item"); + + // + // Constraints + // + solver.Add(item.Sum() == c); + // solver.Add(item[0] * item[1] * item[2] * item[3] == c * 100*100*100); + // solver.Add(item.Prod() == c * 100*100*100); + solver.Add(MyProd(item, c * 100*100*100)); + + + // Symmetry breaking + Decreasing(solver, item); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(item, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(item[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/hidato_table.cs b/examples/dotnet/csharp/hidato_table.cs new file mode 100644 index 0000000000..3bb4554c3e --- /dev/null +++ b/examples/dotnet/csharp/hidato_table.cs @@ -0,0 +1,287 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class HidatoTable +{ + + + /* + * Build closeness pairs for consecutive numbers. + * + * Build set of allowed pairs such that two consecutive numbers touch + * each other in the grid. + * + * Returns: + * A list of pairs for allowed consecutive position of numbers. + * + * Args: + * rows: the number of rows in the grid + * cols: the number of columns in the grid + */ + public static IntTupleSet BuildPairs(int rows, int cols) + { + int[] ix = {-1, 0, 1}; + var result_tmp = (from x in Enumerable.Range(0, rows) + from y in Enumerable.Range(0, cols) + from dx in ix + from dy in ix + where + x + dx >= 0 && + x + dx < rows && + y + dy >= 0 && + y + dy < cols && + (dx != 0 || dy != 0) + select new int[] {x * cols + y, (x + dx) * cols + (y + dy)} + ).ToArray(); + + // Convert to len x 2 matrix + int len = result_tmp.Length; + IntTupleSet result = new IntTupleSet(2); + foreach(int[] r in result_tmp) { + result.Insert(r); + } + return result; + } + + + /** + * + * Hidato puzzle in Google CP Solver. + * + * http://www.hidato.com/ + * """ + * Puzzles start semi-filled with numbered tiles. + * The first and last numbers are circled. + * Connect the numbers together to win. Consecutive + * number must touch horizontally, vertically, or + * diagonally. + * """ + * + * This is a port of the Python model hidato_table.py + * made by Laurent Perron (using AllowedAssignments), + * based on my (much slower) model hidato.py. + * + */ + private static void Solve(int model = 1) + { + Solver solver = new Solver("HidatoTable"); + + // + // models, a 0 indicates an open cell which number is not yet known. + // + + int[,] puzzle = null; + if (model == 1) { + + // Simple problem + + // Solution 1: + // 6 7 9 + // 5 2 8 + // 1 4 3 + int[,] puzzle1 = {{6, 0, 9}, + {0, 2, 8}, + {1, 0, 0}}; + puzzle = puzzle1; + + } else if (model == 2) { + + int[,] puzzle2 = {{0, 44, 41, 0, 0, 0, 0}, + {0, 43, 0, 28, 29, 0, 0}, + {0, 1, 0, 0, 0, 33, 0}, + {0, 2, 25, 4, 34, 0, 36}, + {49, 16, 0, 23, 0, 0, 0}, + {0, 19, 0, 0, 12, 7, 0}, + {0, 0, 0, 14, 0, 0, 0}}; + puzzle = puzzle2; + + } else if (model == 3) { + // Problems from the book: + // Gyora Bededek: "Hidato: 2000 Pure Logic Puzzles" + // Problem 1 (Practice) + int[,] puzzle3 = {{0, 0, 20, 0, 0}, + {0, 0, 0, 16, 18}, + {22, 0, 15, 0, 0}, + {23, 0, 1, 14, 11}, + {0, 25, 0, 0, 12}}; + puzzle = puzzle3; + + } else if (model == 4) { + // problem 2 (Practice) + int[,] puzzle4 = {{0, 0, 0, 0, 14}, + {0, 18, 12, 0, 0}, + {0, 0, 17, 4, 5}, + {0, 0, 7, 0, 0}, + {9, 8, 25, 1, 0}}; + puzzle = puzzle4; + + } else if (model == 5) { + // problem 3 (Beginner) + int[,] puzzle5 = {{0, 26, 0, 0, 0, 18}, + {0, 0, 27, 0, 0, 19}, + {31, 23, 0, 0, 14, 0}, + {0, 33, 8, 0, 15, 1}, + {0, 0, 0, 5, 0, 0}, + {35, 36, 0, 10, 0, 0}}; + puzzle = puzzle5; + + } else if (model == 6) { + // Problem 15 (Intermediate) + int[,] puzzle6 = {{64, 0, 0, 0, 0, 0, 0, 0}, + {1, 63, 0, 59, 15, 57, 53, 0}, + {0, 4, 0, 14, 0, 0, 0, 0}, + {3, 0, 11, 0, 20, 19, 0, 50}, + {0, 0, 0, 0, 22, 0, 48, 40}, + {9, 0, 0, 32, 23, 0, 0, 41}, + {27, 0, 0, 0, 36, 0, 46, 0}, + {28, 30, 0, 35, 0, 0, 0, 0}}; + puzzle = puzzle6; + } + + int r = puzzle.GetLength(0); + int c = puzzle.GetLength(1); + + Console.WriteLine(); + Console.WriteLine("----- Solving problem {0} -----", model); + Console.WriteLine(); + + PrintMatrix(puzzle); + + // + // Decision variables + // + IntVar[] positions = solver.MakeIntVarArray(r*c, 0, r * c - 1, "p"); + + + // + // Constraints + // + solver.Add(positions.AllDifferent()); + + // + // Fill in the clues + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (puzzle[i,j] > 0) { + solver.Add(positions[puzzle[i,j] - 1] == i * c + j); + } + } + } + + // Consecutive numbers much touch each other in the grid. + // We use an allowed assignment constraint to model it. + IntTupleSet close_tuples = BuildPairs(r, c); + for(int k = 1; k < r * c - 1; k++) { + IntVar[] tmp = new IntVar[] {positions[k], positions[k + 1]}; + solver.Add(tmp.AllowedAssignments(close_tuples)); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(positions, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_solution = 0; + while (solver.NextSolution()) { + num_solution++; + PrintOneSolution(positions, r, c, num_solution); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + // Print the current solution + public static void PrintOneSolution(IntVar[] positions, + int rows, + int cols, + int num_solution) + { + + Console.WriteLine("Solution {0}", num_solution); + + // Create empty board + int[,] board = new int[rows, cols]; + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + board[i,j] = 0; + } + } + + // Fill board with solution value + for(int k = 0; k < rows*cols; k++) { + int position = (int)positions[k].Value(); + board[position / cols, position % cols] = k + 1; + } + + PrintMatrix(board); + + } + + + // Pretty print of the matrix + public static void PrintMatrix(int[,] game) + { + int rows = game.GetLength(0); + int cols = game.GetLength(1); + + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + if (game[i,j] == 0) { + Console.Write(" ."); + } else { + Console.Write(" {0,2}", game[i,j] ); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + + + public static void Main(String[] args) + { + int model = 1; + if (args.Length > 0) { + + model = Convert.ToInt32(args[0]); + Solve(model); + + } else { + + for(int m = 1; m <= 6; m++) { + Solve(m); + } + } + + } +} diff --git a/examples/dotnet/csharp/jobshop_ft06_sat.cs b/examples/dotnet/csharp/jobshop_ft06_sat.cs new file mode 100644 index 0000000000..bfce14d92a --- /dev/null +++ b/examples/dotnet/csharp/jobshop_ft06_sat.cs @@ -0,0 +1,141 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class JobshopFt06Sat +{ + + public struct Task + { + public Task(IntVar s, IntVar e, IntervalVar i) + { + start = s; + end = e; + interval = i; + } + + public IntVar start; + public IntVar end; + public IntervalVar interval; + } + + static void Solve() + { + int[,] durations = new int[,] { {1, 3, 6, 7, 3, 6}, + {8, 5, 10, 10, 10, 4}, + {5, 4, 8, 9, 1, 7}, + {5, 5, 5, 3, 8, 9}, + {9, 3, 5, 4, 3, 1}, + {3, 3, 9, 10, 4, 1} }; + int[,] machines = new int[,] { {2, 0, 1, 3, 5, 4}, + {1, 2, 4, 5, 0, 3}, + {2, 3, 5, 0, 1, 4}, + {1, 0, 2, 3, 4, 5}, + {2, 1, 4, 5, 0, 3}, + {1, 3, 5, 0, 4, 2} }; + + int num_jobs = durations.GetLength(0); + int num_machines = durations.GetLength(1); + var all_jobs = Enumerable.Range(0, num_jobs); + var all_machines = Enumerable.Range(0, num_machines); + + int horizon = 0; + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + horizon += durations[j, m]; + } + } + + + // Creates the model. + CpModel model = new CpModel(); + + // Creates jobs. + Task[,] all_tasks = new Task[num_jobs, num_machines]; + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + IntVar start_var = model.NewIntVar( + 0, horizon, String.Format("start_{0}_{1}", j, m)); + int duration = durations[j, m]; + IntVar end_var = model.NewIntVar( + 0, horizon, String.Format("end_{0}_{1}", j, m)); + IntervalVar interval_var = model.NewIntervalVar( + start_var, duration, end_var, + String.Format("interval_{0}_{1}", j, m)); + all_tasks[j, m] = new Task(start_var, end_var, interval_var); + } + } + + // Create disjuctive constraints. + List[] machine_to_jobs = new List[num_machines]; + foreach (int m in all_machines) + { + machine_to_jobs[m] = new List(); + } + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + machine_to_jobs[machines[j, m]].Add(all_tasks[j, m].interval); + } + } + foreach (int m in all_machines) + { + model.AddNoOverlap(machine_to_jobs[m]); + } + + // Precedences inside a job. + foreach (int j in all_jobs) + { + for (int k = 0; k < num_machines - 1; ++k) + { + model.Add(all_tasks[j, k + 1].start >= all_tasks[j, k].end); + } + } + + // Makespan objective. + IntVar[] all_ends = new IntVar[num_jobs]; + foreach (int j in all_jobs) + { + all_ends[j] = all_tasks[j, num_machines - 1].end; + } + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, all_ends); + model.Minimize(makespan); + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + // Display a few solutions picked at random. + CpSolverStatus status = solver.Solve(model); + + // Statistics. + Console.WriteLine("Statistics"); + Console.WriteLine(String.Format(" - solve status : {0}", status)); + Console.WriteLine(" - makespan : " + solver.ObjectiveValue); + Console.WriteLine(" - conflicts : " + solver.NumConflicts()); + Console.WriteLine(" - branches : " + solver.NumBranches()); + Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); + } + + static void Main() { + Solve(); + } +} diff --git a/examples/dotnet/csharp/just_forgotten.cs b/examples/dotnet/csharp/just_forgotten.cs new file mode 100644 index 0000000000..6281ec5d5b --- /dev/null +++ b/examples/dotnet/csharp/just_forgotten.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class JustForgotten +{ + + + + /** + * + * Just forgotten puzzle (Enigma 1517) in Google CP Solver. + * + * From http://www.f1compiler.com/samples/Enigma 201517.f1.html + * """ + * Enigma 1517 Bob Walker, New Scientist magazine, October 25, 2008. + * + * Joe was furious when he forgot one of his bank account numbers. + * He remembered that it had all the digits 0 to 9 in some order, + * so he tried the following four sets without success: + * + * 9 4 6 2 1 5 7 8 3 0 + * 8 6 0 4 3 9 1 2 5 7 + * 1 6 4 0 2 9 7 8 5 3 + * 6 8 2 4 3 1 9 0 7 5 + * + * When Joe finally remembered his account number, he realised that + * in each set just four of the digits were in their correct position + * and that, if one knew that, it was possible to work out his + * account number. What was it? + * """ + * + * Also see http://www.hakank.org/google_or_tools/just_forgotten.py + * + */ + private static void Solve() + { + Solver solver = new Solver("JustForgotten"); + + + int rows = 4; + int cols = 10; + + // The four tries + int[,] a = {{9,4,6,2,1,5,7,8,3,0}, + {8,6,0,4,3,9,1,2,5,7}, + {1,6,4,0,2,9,7,8,5,3}, + {6,8,2,4,3,1,9,0,7,5}}; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(cols, 0, 9, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + for(int r = 0; r < rows; r++) { + solver.Add( (from c in Enumerable.Range(0, cols) + select x[c] == a[r,c]).ToArray().Sum() == 4); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("Account number:"); + for(int j = 0; j < cols; j++) { + Console.Write(x[j].Value() + " "); + } + Console.WriteLine("\n"); + Console.WriteLine("The four tries, where '!' represents a correct digit:"); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + String c = " "; + if (a[i,j] == x[j].Value()) { + c = "!"; + } + Console.Write("{0}{1} ", a[i,j], c); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/kakuro.cs b/examples/dotnet/csharp/kakuro.cs new file mode 100644 index 0000000000..e7a53b672d --- /dev/null +++ b/examples/dotnet/csharp/kakuro.cs @@ -0,0 +1,219 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Kakuro +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + // ensure that the values are positive + int len = cc.Length / 2; + for(int i = 0; i < len; i++) { + solver.Add(x[cc[i*2]-1,cc[i*2+1]-1] >= 1); + } + + // sum the numbers + solver.Add( (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]) + .ToArray().Sum() == res); + } + + + + /** + * + * Kakuru puzzle. + * + * http://en.wikipedia.org/wiki/Kakuro + * """ + * The object of the puzzle is to insert a digit from 1 to 9 inclusive + * into each white cell such that the sum of the numbers in each entry + * matches the clue associated with it and that no digit is duplicated in + * any entry. It is that lack of duplication that makes creating Kakuro + * puzzles with unique solutions possible, and which means solving a Kakuro + * puzzle involves investigating combinations more, compared to Sudoku in + * which the focus is on permutations. There is an unwritten rule for + * making Kakuro puzzles that each clue must have at least two numbers + * that add up to it. This is because including one number is mathematically + * trivial when solving Kakuro puzzles; one can simply disregard the + * number entirely and subtract it from the clue it indicates. + * """ + * + * This model solves the problem at the Wikipedia page. + * For a larger picture, see + * http://en.wikipedia.org/wiki/File:Kakuro_black_box.svg + * + * The solution: + * 9 7 0 0 8 7 9 + * 8 9 0 8 9 5 7 + * 6 8 5 9 7 0 0 + * 0 6 1 0 2 6 0 + * 0 0 4 6 1 3 2 + * 8 9 3 1 0 1 4 + * 3 1 2 0 0 2 1 + * + * Also see http://www.hakank.org/or-tools/kakuro.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("Kakuro"); + + // size of matrix + int n = 7; + + // segments: + // sum, the segments + // Note: this is 1-based + int[][] problem = + { + new int[] {16, 1,1, 1,2}, + new int[] {24, 1,5, 1,6, 1,7}, + new int[] {17, 2,1, 2,2}, + new int[] {29, 2,4, 2,5, 2,6, 2,7}, + new int[] {35, 3,1, 3,2, 3,3, 3,4, 3,5}, + new int[] { 7, 4,2, 4,3}, + new int[] { 8, 4,5, 4,6}, + new int[] {16, 5,3, 5,4, 5,5, 5,6, 5,7}, + new int[] {21, 6,1, 6,2, 6,3, 6,4}, + new int[] { 5, 6,6, 6,7}, + new int[] { 6, 7,1, 7,2, 7,3}, + new int[] { 3, 7,6, 7,7}, + + new int[] {23, 1,1, 2,1, 3,1}, + new int[] {30, 1,2, 2,2, 3,2, 4,2}, + new int[] {27, 1,5, 2,5, 3,5, 4,5, 5,5}, + new int[] {12, 1,6, 2,6}, + new int[] {16, 1,7, 2,7}, + new int[] {17, 2,4, 3,4}, + new int[] {15, 3,3, 4,3, 5,3, 6,3, 7,3}, + new int[] {12, 4,6, 5,6, 6,6, 7,6}, + new int[] { 7, 5,4, 6,4}, + new int[] { 7, 5,7, 6,7, 7,7}, + new int[] {11, 6,1, 7,1}, + new int[] {10, 6,2, 7,2} + + }; + + + int num_p = 24; // Number of segments + + // The blanks + // Note: 1-based + int[,] blanks = { + {1,3}, {1,4}, + {2,3}, + {3,6}, {3,7}, + {4,1}, {4,4}, {4,7}, + {5,1}, {5,2}, + {6,5}, + {7,4}, {7,5} + }; + + int num_blanks = blanks.GetLength(0); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // fill the blanks with 0 + for(int i = 0; i < num_blanks; i++) { + solver.Add(x[blanks[i,0]-1,blanks[i,1]-1]==0); + } + + for(int i = 0; i < num_p; i++) { + int[] segment = problem[i]; + + // Remove the sum from the segment + int[] s2 = new int[segment.Length-1]; + for(int j = 1; j < segment.Length; j++) { + s2[j-1] = segment[j]; + } + + // sum this segment + calc(solver, s2, x, segment[0]); + + // all numbers in this segment must be distinct + int len = segment.Length / 2; + solver.Add( (from j in Enumerable.Range(0, len) + select x[s2[j * 2] - 1, s2[j * 2 + 1] - 1]) + .ToArray().AllDifferent()); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + int v = (int)x[i,j].Value(); + if (v > 0) { + Console.Write(v + " "); + } else { + Console.Write(" "); + } + } + Console.WriteLine(); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/examples/dotnet/csharp/kenken2.cs b/examples/dotnet/csharp/kenken2.cs new file mode 100644 index 0000000000..a322f15a38 --- /dev/null +++ b/examples/dotnet/csharp/kenken2.cs @@ -0,0 +1,241 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class KenKen2 +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + int ccLen = cc.Length; + if (ccLen == 4) { + + // for two operands there's + // a lot of possible variants + IntVar a = x[cc[0]-1, cc[1]-1]; + IntVar b = x[cc[2]-1, cc[3]-1]; + + IntVar r1 = a + b == res; + IntVar r2 = a * b == res; + IntVar r3 = a * res == b; + IntVar r4 = b * res == a; + IntVar r5 = a - b == res; + IntVar r6 = b - a == res; + + solver.Add(r1+r2+r3+r4+r5+r6 >= 1); + + } else { + + // For length > 2 then res is either the sum + // the the product of the segment + + // sum the numbers + int len = cc.Length / 2; + IntVar[] xx = (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]).ToArray(); + + // Sum + IntVar this_sum = xx.Sum() == res; + + // Product + // IntVar this_prod = (xx.Prod() == res).Var(); // don't work + IntVar this_prod; + if (xx.Length == 3) { + this_prod = (x[cc[0]-1,cc[1]-1] * + x[cc[2]-1,cc[3]-1] * + x[cc[4]-1,cc[5]-1]) == res; + } else { + this_prod = (x[cc[0]-1,cc[1]-1] * + x[cc[2]-1,cc[3]-1] * + x[cc[4]-1,cc[5]-1] * + x[cc[6]-1,cc[7]-1]) == res; + + } + + solver.Add(this_sum + this_prod >= 1); + + + } + } + + + + /** + * + * KenKen puzzle. + * + * http://en.wikipedia.org/wiki/KenKen + * """ + * KenKen or KEN-KEN is a style of arithmetic and logical puzzle sharing + * several characteristics with sudoku. The name comes from Japanese and + * is translated as 'square wisdom' or 'cleverness squared'. + * ... + * The objective is to fill the grid in with the digits 1 through 6 such that: + * + * * Each row contains exactly one of each digit + * * Each column contains exactly one of each digit + * * Each bold-outlined group of cells is a cage containing digits which + * achieve the specified result using the specified mathematical operation: + * addition (+), + * subtraction (-), + * multiplication (x), + * and division (/). + * (Unlike in Killer sudoku, digits may repeat within a group.) + * + * ... + * More complex KenKen problems are formed using the principles described + * above but omitting the symbols +, -, x and /, thus leaving them as + * yet another unknown to be determined. + * """ + * + * The solution is: + * + * 5 6 3 4 1 2 + * 6 1 4 5 2 3 + * 4 5 2 3 6 1 + * 3 4 1 2 5 6 + * 2 3 6 1 4 5 + * 1 2 5 6 3 4 + * + * + * Also see http://www.hakank.org/or-tools/kenken2.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("KenKen2"); + + // size of matrix + int n = 6; + IEnumerable RANGE = Enumerable.Range(0, n); + + // For a better view of the problem, see + // http://en.wikipedia.org/wiki/File:KenKenProblem.svg + + // hints + // sum, the hints + // Note: this is 1-based + int[][] problem = + { + new int[] { 11, 1,1, 2,1}, + new int[] { 2, 1,2, 1,3}, + new int[] { 20, 1,4, 2,4}, + new int[] { 6, 1,5, 1,6, 2,6, 3,6}, + new int[] { 3, 2,2, 2,3}, + new int[] { 3, 2,5, 3,5}, + new int[] {240, 3,1, 3,2, 4,1, 4,2}, + new int[] { 6, 3,3, 3,4}, + new int[] { 6, 4,3, 5,3}, + new int[] { 7, 4,4, 5,4, 5,5}, + new int[] { 30, 4,5, 4,6}, + new int[] { 6, 5,1, 5,2}, + new int[] { 9, 5,6, 6,6}, + new int[] { 8, 6,1, 6,2, 6,3}, + new int[] { 2, 6,4, 6,5} + }; + + + int num_p = problem.GetLength(0); // Number of segments + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // + // alldifferent rows and columns + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE select x[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE select x[j,i]).ToArray().AllDifferent()); + + } + + + // Calculate the segments + for(int i = 0; i < num_p; i++) { + + int[] segment = problem[i]; + + // Remove the sum from the segment + int len = segment.Length-1; + int[] s2 = new int[len]; + Array.Copy(segment, 1, s2, 0, len); + + // sum this segment + calc(solver, s2, x, segment[0]); + + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/examples/dotnet/csharp/killer_sudoku.cs b/examples/dotnet/csharp/killer_sudoku.cs new file mode 100644 index 0000000000..390ec05146 --- /dev/null +++ b/examples/dotnet/csharp/killer_sudoku.cs @@ -0,0 +1,240 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class KillerSudoku +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + // sum the numbers + int len = cc.Length / 2; + solver.Add( (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]).ToArray().Sum() == res); + } + + + + /** + * + * Killer Sudoku. + * + * http://en.wikipedia.org/wiki/Killer_Sudoku + * """ + * Killer sudoku (also killer su doku, sumdoku, sum doku, addoku, or + * samunamupure) is a puzzle that combines elements of sudoku and kakuro. + * Despite the name, the simpler killer sudokus can be easier to solve + * than regular sudokus, depending on the solver's skill at mental arithmetic; + * the hardest ones, however, can take hours to crack. + * + * ... + * + * The objective is to fill the grid with numbers from 1 to 9 in a way that + * the following conditions are met: + * + * - Each row, column, and nonet contains each number exactly once. + * - The sum of all numbers in a cage must match the small number printed + * in its corner. + * - No number appears more than once in a cage. (This is the standard rule + * for killer sudokus, and implies that no cage can include more + * than 9 cells.) + * + * In 'Killer X', an additional rule is that each of the long diagonals + * contains each number once. + * """ + * + * Here we solve the problem from the Wikipedia page, also shown here + * http://en.wikipedia.org/wiki/File:Killersudoku_color.svg + * + * The output is: + * 2 1 5 6 4 7 3 9 8 + * 3 6 8 9 5 2 1 7 4 + * 7 9 4 3 8 1 6 5 2 + * 5 8 6 2 7 4 9 3 1 + * 1 4 2 5 9 3 8 6 7 + * 9 7 3 8 1 6 4 2 5 + * 8 2 1 7 3 9 5 4 6 + * 6 5 9 4 2 8 7 1 3 + * 4 3 7 1 6 5 2 8 9 + * + * Also see http://www.hakank.org/or-tools/killer_sudoku.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("KillerSudoku"); + + // size of matrix + int cell_size = 3; + IEnumerable CELL = Enumerable.Range(0, cell_size); + int n = cell_size*cell_size; + IEnumerable RANGE = Enumerable.Range(0, n); + + // For a better view of the problem, see + // http://en.wikipedia.org/wiki/File:Killersudoku_color.svg + + // hints + // sum, the hints + // Note: this is 1-based + int[][] problem = + { + new int[] { 3, 1,1, 1,2}, + new int[] {15, 1,3, 1,4, 1,5}, + new int[] {22, 1,6, 2,5, 2,6, 3,5}, + new int[] {4, 1,7, 2,7}, + new int[] {16, 1,8, 2,8}, + new int[] {15, 1,9, 2,9, 3,9, 4,9}, + new int[] {25, 2,1, 2,2, 3,1, 3,2}, + new int[] {17, 2,3, 2,4}, + new int[] { 9, 3,3, 3,4, 4,4}, + new int[] { 8, 3,6, 4,6, 5,6}, + new int[] {20, 3,7, 3,8, 4,7}, + new int[] { 6, 4,1, 5,1}, + new int[] {14, 4,2, 4,3}, + new int[] {17, 4,5, 5,5, 6,5}, + new int[] {17, 4,8, 5,7, 5,8}, + new int[] {13, 5,2, 5,3, 6,2}, + new int[] {20, 5,4, 6,4, 7,4}, + new int[] {12, 5,9, 6,9}, + new int[] {27, 6,1, 7,1, 8,1, 9,1}, + new int[] { 6, 6,3, 7,2, 7,3}, + new int[] {20, 6,6, 7,6, 7,7}, + new int[] { 6, 6,7, 6,8}, + new int[] {10, 7,5, 8,4, 8,5, 9,4}, + new int[] {14, 7,8, 7,9, 8,8, 8,9}, + new int[] { 8, 8,2, 9,2}, + new int[] {16, 8,3, 9,3}, + new int[] {15, 8,6, 8,7}, + new int[] {13, 9,5, 9,6, 9,7}, + new int[] {17, 9,8, 9,9} + + }; + + + int num_p = 29; // Number of segments + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // + // The first three constraints is the same as for sudokus.cs + // + // alldifferent rows and columns + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE + select x[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE + select x[j,i]).ToArray().AllDifferent()); + + } + + // cells + foreach(int i in CELL) { + foreach(int j in CELL) { + solver.Add( (from di in CELL + from dj in CELL + select x[i*cell_size+di, j*cell_size+dj] + ).ToArray().AllDifferent()); + } + } + + + // Sum the segments and ensure alldifferent + for(int i = 0; i < num_p; i++) { + int[] segment = problem[i]; + + // Remove the sum from the segment + int[] s2 = new int[segment.Length-1]; + for(int j = 1; j < segment.Length; j++) { + s2[j-1] = segment[j]; + } + + // sum this segment + calc(solver, s2, x, segment[0]); + + // all numbers in this segment must be distinct + int len = segment.Length / 2; + solver.Add( (from j in Enumerable.Range(0, len) + select x[s2[j*2]-1, s2[j*2+1]-1]) + .ToArray().AllDifferent()); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + int v = (int)x[i,j].Value(); + if (v > 0) { + Console.Write(v + " "); + } else { + Console.Write(" "); + } + } + Console.WriteLine(); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/examples/dotnet/csharp/labeled_dice.cs b/examples/dotnet/csharp/labeled_dice.cs new file mode 100644 index 0000000000..5b273cfc2d --- /dev/null +++ b/examples/dotnet/csharp/labeled_dice.cs @@ -0,0 +1,184 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class LabeledDice +{ + + /** + * + * Labeled dice problem. + * + * From Jim Orlin 'Colored letters, labeled dice: a logic puzzle' + * http://jimorlin.wordpress.com/2009/02/17/colored-letters-labeled-dice-a-logic-puzzle/ + * """ + * My daughter Jenn bough a puzzle book, and showed me a cute puzzle. There + * are 13 words as follows: BUOY, CAVE, CELT, FLUB, FORK, HEMP, JUDY, + * JUNK, LIMN, QUIP, SWAG, VISA, WISH. + * + * There are 24 different letters that appear in the 13 words. The question + * is: can one assign the 24 letters to 4 different cubes so that the + * four letters of each word appears on different cubes. (There is one + * letter from each word on each cube.) It might be fun for you to try + * it. I'll give a small hint at the end of this post. The puzzle was + * created by Humphrey Dudley. + * """ + * + * Jim Orlin's followup 'Update on Logic Puzzle': + * http://jimorlin.wordpress.com/2009/02/21/update-on-logic-puzzle/ + * + * + * Also see http://www.hakank.org/or-tools/labeled_dice.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("LabeledDice"); + + // + // Data + // + int n = 4; + int m = 24; + + int A = 0; + int B = 1; + int C = 2; + int D = 3; + int E = 4; + int F = 5; + int G = 6; + int H = 7; + int I = 8; + int J = 9; + int K = 10; + int L = 11; + int M = 12; + int N = 13; + int O = 14; + int P = 15; + int Q = 16; + int R = 17; + int S = 18; + int T = 19; + int U = 20; + int V = 21; + int W = 22; + int Y = 23; + + + String[] letters_str = {"A","B","C","D","E","F","G","H","I","J","K","L","M", + "N","O","P","Q","R","S","T","U","V","W","Y"}; + + int num_words = 13; + int[,] words = + { + {B,U,O,Y}, + {C,A,V,E}, + {C,E,L,T}, + {F,L,U,B}, + {F,O,R,K}, + {H,E,M,P}, + {J,U,D,Y}, + {J,U,N,K}, + {L,I,M,N}, + {Q,U,I,P}, + {S,W,A,G}, + {V,I,S,A}, + {W,I,S,H} + }; + + + // + // Decision variables + // + IntVar[] dice = solver.MakeIntVarArray(m, 0, n-1, "dice"); + IntVar[] gcc = solver.MakeIntVarArray(n, 6, 6, "gcc"); + + // + // Constraints + // + + + // the letters in a word must be on a different die + for(int i = 0; i < num_words; i++) { + solver.Add( (from j in Enumerable.Range(0, n) + select dice[words[i,j]] + ).ToArray().AllDifferent()); + } + + // there must be exactly 6 letters of each die + /* + for(int i = 0; i < n; i++) { + solver.Add( ( from j in Enumerable.Range(0, m) + select (dice[j] == i) + ).ToArray().Sum() == 6 ); + } + */ + // Use Distribute (Global Cardinality Count) instead. + solver.Add(dice.Distribute(gcc)); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(dice, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int d = 0; d < n; d++) { + Console.Write("die {0}: ", d); + for(int i = 0; i < m; i++) { + if (dice[i].Value() == d) { + Console.Write(letters_str[i]); + } + } + Console.WriteLine(); + } + + Console.WriteLine("The words with the cube label:"); + for(int i = 0; i < num_words; i++) { + for(int j = 0; j < n; j++) { + Console.Write("{0} ({1})", letters_str[words[i,j]], dice[words[i,j]].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/langford.cs b/examples/dotnet/csharp/langford.cs new file mode 100644 index 0000000000..d29d56e3be --- /dev/null +++ b/examples/dotnet/csharp/langford.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Langford +{ + + /** + * + * Langford number problem. + * See http://www.hakank.org/or-tools/langford.py + * + */ + private static void Solve(int k = 8, int num_sol = 0) + { + + Solver solver = new Solver("Langford"); + + Console.WriteLine("k: {0}", k); + + // + // data + // + int p = 2*k; + + // + // Decision variables + // + IntVar[] position = solver.MakeIntVarArray(p, 0, p-1, "position"); + IntVar[] solution = solver.MakeIntVarArray(p, 1, k, "solution"); + + // + // Constraints + // + solver.Add(position.AllDifferent()); + + for(int i = 1; i <= k; i++) { + solver.Add(position[i+k-1] - (position[i-1] + solver.MakeIntVar(i+1,i+1)) == 0); + solver.Add(solution.Element(position[i-1]) == i); + solver.Add(solution.Element(position[k+i-1]) == i); + } + + // Symmetry breaking + solver.Add(solution[0] < solution[2*k-1]); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(position, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + Console.Write("solution : "); + for(int i = 0; i < p; i++) { + Console.Write(solution[i].Value() + " "); + } + Console.WriteLine(); + num_solutions++; + if (num_sol > 0 && num_solutions >= num_sol) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int k = 8; + int num_sol = 0; // 0: print all solutions + + if (args.Length > 0) { + k = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num_sol = Convert.ToInt32(args[1]); + } + + Solve(k, num_sol); + + } +} diff --git a/examples/dotnet/csharp/least_diff.cs b/examples/dotnet/csharp/least_diff.cs new file mode 100644 index 0000000000..f525265e8c --- /dev/null +++ b/examples/dotnet/csharp/least_diff.cs @@ -0,0 +1,91 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class LeastDiff +{ + /** + * + * Solve the Least diff problem + * For more info, see http://www.hakank.org/google_or_tools/least_diff.py + * + */ + private static void Solve() + { + Solver solver = new Solver("LeastDiff"); + + // + // Decision variables + // + IntVar A = solver.MakeIntVar(0, 9, "A"); + IntVar B = solver.MakeIntVar(0, 9, "B"); + IntVar C = solver.MakeIntVar(0, 9, "C"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar F = solver.MakeIntVar(0, 9, "F"); + IntVar G = solver.MakeIntVar(0, 9, "G"); + IntVar H = solver.MakeIntVar(0, 9, "H"); + IntVar I = solver.MakeIntVar(0, 9, "I"); + IntVar J = solver.MakeIntVar(0, 9, "J"); + + IntVar[] all = new IntVar[] {A,B,C,D,E,F,G,H,I,J}; + int[] coeffs = {10000,1000,100,10,1}; + IntVar x = new IntVar[]{A,B,C,D,E}.ScalProd(coeffs).Var(); + IntVar y = new IntVar[]{F,G,H,I,J}.ScalProd(coeffs).Var(); + IntVar diff = (x - y).VarWithName("diff"); + + + // + // Constraints + // + solver.Add(all.AllDifferent()); + solver.Add(A > 0); + solver.Add(F > 0); + solver.Add(diff > 0); + + + // + // Objective + // + OptimizeVar obj = diff.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("{0} - {1} = {2} ({3}",x.Value(), y.Value(), diff.Value(), diff.ToString()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/lectures.cs b/examples/dotnet/csharp/lectures.cs new file mode 100644 index 0000000000..1427233fd6 --- /dev/null +++ b/examples/dotnet/csharp/lectures.cs @@ -0,0 +1,193 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class Lectures +{ + + /** + * + * 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? + * """ + * + * Note: This can be seen as a coloring problem. + * + * Also see http://www.hakank.org/or-tools/lectures.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Lectures"); + + // + // The schedule requirements: + // lecture a cannot be held at the same time as b + // Note: 1-based (compensated in the constraints). + int[,] g = + { + {1, 2}, + {1, 4}, + {3, 5}, + {2, 6}, + {4, 5}, + {5, 6}, + {1, 6} + }; + + // number of nodes + int n = 6; + + // number of edges + int edges = g.GetLength(0); + + // + // Decision variables + // + // + // declare variables + // + IntVar[] v = solver.MakeIntVarArray(n, 0, n-1,"v"); + + // Maximum color (hour) to minimize. + // Note: since C# is 0-based, the + // number of colors is max_c+1. + IntVar max_c = v.Max().VarWithName("max_c"); + + + // + // Constraints + // + + // Ensure that there are no clashes + // also, adjust to 0-base. + for(int i = 0; i < edges; i++) { + 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 + // + OptimizeVar obj = max_c.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(v, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("\nmax hours: {0}", max_c.Value()+1); + Console.WriteLine("v: " + + String.Join(" ", (from i in Enumerable.Range(0, n) + select v[i].Value()).ToArray())); + for(int i = 0; i < n; i++) { + Console.WriteLine("Lecture {0} at {1}h", i, v[i].Value()); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + // Print the current solution + public static void PrintOneSolution(IntVar[] positions, + int rows, + int cols, + int num_solution) + { + + Console.WriteLine("Solution {0}", num_solution); + + // Create empty board + int[,] board = new int[rows, cols]; + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + board[i,j] = 0; + } + } + + // Fill board with solution value + for(int k = 0; k < rows*cols; k++) { + int position = (int)positions[k].Value(); + board[position / cols, position % cols] = k + 1; + } + + PrintMatrix(board); + + } + + + // Pretty print of the matrix + public static void PrintMatrix(int[,] game) + { + int rows = game.GetLength(0); + int cols = game.GetLength(1); + + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + if (game[i,j] == 0) { + Console.Write(" ."); + } else { + Console.Write(" {0,2}", game[i,j] ); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/magic_sequence.cs b/examples/dotnet/csharp/magic_sequence.cs new file mode 100644 index 0000000000..609fcca2d3 --- /dev/null +++ b/examples/dotnet/csharp/magic_sequence.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class MagicSequence +{ + + /** + * + * Magic sequence problem. + * + * This is a port of the Python model + * https://code.google.com/p/or-tools/source/browse/trunk/python/magic_sequence_distribute.py + * """ + * This models aims at building a sequence of numbers such that the number of + * occurrences of i in this sequence is equal to the value of the ith number. + * It uses an aggregated formulation of the count expression called + * distribute(). + * """ + * + */ + private static void Solve(int size) + { + + Solver solver = new Solver("MagicSequence"); + + Console.WriteLine("\nSize: {0}", size); + + // + // data + // + int[] all_values = new int[size]; + for (int i = 0; i < size; i++) { + all_values[i] = i; + } + + // + // Decision variables + // + IntVar[] all_vars = solver.MakeIntVarArray(size, 0, size - 1, "vars"); + + // + // Constraints + // + solver.Add(all_vars.Distribute(all_values, all_vars)); + solver.Add(all_vars.Sum() == size); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < size; i++) { + Console.Write(all_vars[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + if (args.Length > 0) { + + int size = Convert.ToInt32(args[0]); + Solve(size); + + } else { + // Let's test some diferent sizes + foreach(int i in new int[] {2, 10, 100, 200, 500}) { + Solve(i); + } + + } + + + } +} diff --git a/examples/dotnet/csharp/magic_square.cs b/examples/dotnet/csharp/magic_square.cs new file mode 100644 index 0000000000..99e679e3f4 --- /dev/null +++ b/examples/dotnet/csharp/magic_square.cs @@ -0,0 +1,140 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class MagicSquare +{ + + /** + * + * Solves the Magic Square problem. + * See http://www.hakank.org/or-tools/magic_square.py + * + */ + private static void Solve(int n = 4, int num = 0, int print = 1) + { + Solver solver = new Solver("MagicSquare"); + + Console.WriteLine("n: {0}", n); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n*n, "x"); + // for the branching + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + long s = (n * (n * n + 1)) / 2; + Console.WriteLine("s: " + s); + + IntVar[] diag1 = new IntVar[n]; + IntVar[] diag2 = new IntVar[n]; + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + } + // sum row to s + solver.Add(row.Sum() == s); + + diag1[i] = x[i,i]; + diag2[i] = x[i,n - i - 1]; + } + + // sum diagonals to s + solver.Add(diag1.Sum() == s); + solver.Add(diag2.Sum() == s); + + // sum columns to s + for(int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for(int i = 0; i < n; i++) { + col[i] = x[i,j]; + } + solver.Add(col.Sum() == s); + } + + // all are different + solver.Add(x_flat.AllDifferent()); + + // symmetry breaking: upper left is 1 + // solver.Add(x[0,0] == 1); + + + // + // Search + // + + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_CENTER_VALUE); + + + solver.NewSearch(db); + + int c = 0; + while (solver.NextSolution()) { + if (print != 0) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + c++; + if (num > 0 && c >= num) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 4; + int num = 0; + int print = 1; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + print = Convert.ToInt32(args[2]); + } + + Solve(n, num, print); + } +} diff --git a/examples/dotnet/csharp/magic_square_and_cards.cs b/examples/dotnet/csharp/magic_square_and_cards.cs new file mode 100644 index 0000000000..a76039f2d3 --- /dev/null +++ b/examples/dotnet/csharp/magic_square_and_cards.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class MagicSquareAndCards +{ + + /** + * + * Magic squares and cards problem. + * + * Martin Gardner (July 1971) + * """ + * Allowing duplicates values, what is the largest constant sum for an order-3 + * magic square that can be formed with nine cards from the deck. + * """ + * + * + * Also see http://www.hakank.org/or-tools/magic_square_and_cards.py + * + */ + private static void Solve(int n=3) + { + + Solver solver = new Solver("MagicSquareAndCards"); + + IEnumerable RANGE = Enumerable.Range(0, n); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, 13, "x"); + IntVar[] x_flat = x.Flatten(); + + IntVar s = solver.MakeIntVar(1, 13*4, "s"); + IntVar[] counts = solver.MakeIntVarArray(14, 0, 4, "counts"); + + // + // Constraints + // + + solver.Add(x_flat.Distribute(counts)); + + // the standard magic square constraints (sans all_different) + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE select x[i,j]).ToArray().Sum() == s); + + // columns + solver.Add( (from j in RANGE select x[j,i]).ToArray().Sum() == s); + } + + // diagonals + solver.Add( (from i in RANGE select x[i,i]).ToArray().Sum() == s); + solver.Add( (from i in RANGE select x[i,n-i-1]).ToArray().Sum() == s); + + + // redundant constraint + solver.Add(counts.Sum() == n*n); + + + // + // Objective + // + OptimizeVar obj = s.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("s: {0}", s.Value()); + Console.Write("counts:"); + for(int i = 0; i < 14; i++) { + Console.Write(counts[i].Value() + " "); + } + Console.WriteLine(); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 3; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/map.cs b/examples/dotnet/csharp/map.cs new file mode 100644 index 0000000000..bafba79858 --- /dev/null +++ b/examples/dotnet/csharp/map.cs @@ -0,0 +1,96 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Map +{ + /** + * + * Solves a simple map coloring problem. + * + * See http://www.hakank.org/google_or_tools/map.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Map"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + // + // Decision variables + // + IntVar[] color = solver.MakeIntVarArray(n, 1, max_num_colors, "color"); + + // + // Constraints + // + solver.Add(color[France] != color[Belgium]); + solver.Add(color[France] != color[Luxembourg]); + solver.Add(color[France] != color[Germany]); + solver.Add(color[Luxembourg] != color[Germany]); + solver.Add(color[Luxembourg] != color[Belgium]); + solver.Add(color[Belgium] != color[Netherlands]); + solver.Add(color[Belgium] != color[Germany]); + solver.Add(color[Germany] != color[Netherlands]); + solver.Add(color[Germany] != color[Denmark]); + + // Symmetry breaking + solver.Add(color[Belgium] == 1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(color, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("colors: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", color[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/map2.cs b/examples/dotnet/csharp/map2.cs new file mode 100644 index 0000000000..b58eaa1411 --- /dev/null +++ b/examples/dotnet/csharp/map2.cs @@ -0,0 +1,107 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Map2 +{ + /** + * + * Solves a simple map coloring problem. + * + * Alternative version, using a matrix to represent + * the neighbours. + * + * See http://www.hakank.org/google_or_tools/map.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("Map2"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + int[,] neighbours = {{France, Belgium}, + {France, Luxembourg}, + {France, Germany}, + {Luxembourg, Germany}, + {Luxembourg, Belgium}, + {Belgium, Netherlands}, + {Belgium, Germany}, + {Germany, Netherlands}, + {Germany, Denmark}}; + + + + // + // Decision variables + // + IntVar[] color = solver.MakeIntVarArray(n, 1, max_num_colors, "color"); + + // + // Constraints + // + for(int i = 0; i < neighbours.GetLength(0); i++) { + solver.Add(color[neighbours[i,0]] != color[neighbours[i,1]]); + } + + // Symmetry breaking + solver.Add(color[Belgium] == 1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(color, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("colors: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", color[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/marathon2.cs b/examples/dotnet/csharp/marathon2.cs new file mode 100644 index 0000000000..4c63f39da1 --- /dev/null +++ b/examples/dotnet/csharp/marathon2.cs @@ -0,0 +1,144 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Marathon2 +{ + /** + * + * Marathon puzzle. + * + * 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 + * """ + * + * Also see http://www.hakank.org/or-tools/marathon2.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Marathon2"); + + // + // Data + // + int n = 6; + String[] runners_str = {"Dominique", "Ignace", "Naren", + "Olivier", "Philippe", "Pascal"}; + + + // + // Decision variables + // + IntVar[] runners = solver.MakeIntVarArray(n, 1, n, "runners"); + IntVar Dominique = runners[0]; + IntVar Ignace = runners[1]; + IntVar Naren = runners[2]; + IntVar Olivier = runners[3]; + IntVar Philippe = runners[4]; + IntVar Pascal = runners[5]; + + // + // Constraints + // + solver.Add(runners.AllDifferent()); + + // 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); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(runners, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + int[] runners_val = new int[n]; + Console.Write("runners: "); + for(int i = 0; i < n; i++) { + runners_val[i] = (int)runners[i].Value(); + Console.Write(runners_val[i] + " "); + } + Console.WriteLine("\nPlaces:"); + for(int i = 1; i < n+1; i++) { + for(int j = 0; j < n; j++) { + if (runners_val[j] == i) { + Console.WriteLine("{0}: {1}", i, runners_str[j]); + } + } + } + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/max_flow_taha.cs b/examples/dotnet/csharp/max_flow_taha.cs new file mode 100644 index 0000000000..8bfc2a473a --- /dev/null +++ b/examples/dotnet/csharp/max_flow_taha.cs @@ -0,0 +1,170 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class MaxFlowTaha +{ + /** + * + * Max flow problem. + * + * From Taha "Introduction to Operations Research", Example 6.4-2 + * + * Translated from the AMPL code at + * http://taha.ineg.uark.edu/maxflo.txt + * + * Also see http://www.hakank.org/or-tools/max_flow_taha.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("MaxFlowTaha"); + + // + // Data + // + int n = 5; + int start = 0; + int end = n-1; + + IEnumerable NODES = Enumerable.Range(0, n); + + // cost matrix + int[,] c = { + {0, 20, 30, 10, 0}, + {0, 0, 40, 0, 30}, + {0, 0, 0, 10, 20}, + {0, 0, 5, 0, 20}, + {0, 0, 0, 0, 0} + }; + + + + // + // Decision variables + // + IntVar[,] x = new IntVar[n,n]; + foreach(int i in NODES) { + foreach(int j in NODES) { + x[i,j] = solver.MakeIntVar(0, c[i,j], "x"); + } + } + + IntVar[] x_flat = x.Flatten(); + + IntVar[] out_flow = solver.MakeIntVarArray(n, 0, 1000, "out_flow"); + IntVar[] in_flow = solver.MakeIntVarArray(n, 0, 1000, "in_flow"); + IntVar total = solver.MakeIntVar(0, 10000, "total"); + + // + // Constraints + // + solver.Add( (from j in NODES + where c[start,j] > 0 + select x[start,j] + ).ToArray().Sum() == total); + + foreach(int i in NODES) { + + var in_flow_sum = (from j in NODES + where c[j,i] > 0 + select x[j,i] + ); + if (in_flow_sum.Count() > 0) { + solver.Add(in_flow_sum.ToArray().Sum() == in_flow[i]); + } + + var out_flow_sum = (from j in NODES + where c[i,j] > 0 + select x[i,j] + ); + if (out_flow_sum.Count() > 0) { + solver.Add(out_flow_sum.ToArray().Sum() == out_flow[i]); + } + + } + + // in_flow == out_flow + foreach(int i in NODES) { + if (i != start && i != end) { + solver.Add(out_flow[i] == in_flow[i]); + } + } + + var s1 = (from i in NODES where c[i,start] > 0 select x[i,start]); + if (s1.Count() > 0) { + solver.Add(s1.ToArray().Sum() == 0); + } + + var s2 = (from j in NODES where c[end, j] > 0 select x[end,j]); + if (s2.Count() > 0) { + solver.Add(s2.ToArray().Sum() == 0); + } + + + // + // Objective + // + OptimizeVar obj = total.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat.Concat(in_flow).Concat(out_flow).ToArray(), + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("total: {0}",total.Value()); + Console.Write("in_flow : "); + foreach(int i in NODES) { + Console.Write(in_flow[i].Value() + " "); + } + Console.Write("\nout_flow: "); + foreach(int i in NODES) { + Console.Write(out_flow[i].Value() + " "); + } + Console.WriteLine(); + foreach(int i in NODES) { + foreach(int j in NODES) { + Console.Write("{0,2} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/max_flow_winston1.cs b/examples/dotnet/csharp/max_flow_winston1.cs new file mode 100644 index 0000000000..0fa4573b11 --- /dev/null +++ b/examples/dotnet/csharp/max_flow_winston1.cs @@ -0,0 +1,165 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class MaxFlowWinston1 +{ + /** + * + * Max flow problem. + * + * From Winston 'Operations Research', page 420f, 423f + * Sunco Oil example. + * + * + * Also see http://www.hakank.org/or-tools/max_flow_winston1.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("MaxFlowWinston1"); + + // + // Data + // + int n = 5; + IEnumerable NODES = Enumerable.Range(0, n); + + // The arcs + // Note: + // This is 1-based to be compatible with other implementations. + // + int[,] arcs1 = { + {1, 2}, + {1, 3}, + {2, 3}, + {2, 4}, + {3, 5}, + {4, 5}, + {5, 1} + }; + + // Capacities + int [] cap = {2,3,3,4,2,1,100}; + + // Convert arcs to 0-based + int num_arcs = arcs1.GetLength(0); + IEnumerable ARCS = Enumerable.Range(0, num_arcs); + int[,] arcs = new int[num_arcs, 2]; + foreach(int i in ARCS) { + for(int j = 0; j < 2; j++) { + arcs[i,j] = arcs1[i,j] - 1; + } + } + + // Convert arcs to matrix (for sanity checking below) + int[,] mat = new int[num_arcs, num_arcs]; + foreach(int i in NODES) { + foreach(int j in NODES) { + int c = 0; + foreach(int k in ARCS) { + if (arcs[k,0] == i && arcs[k,1] == j) { + c = 1; + } + } + mat[i,j] = c; + } + } + + // + // Decision variables + // + IntVar[,] flow = solver.MakeIntVarMatrix(n, n, 0, 200, "flow"); + IntVar z = flow[n-1, 0].VarWithName("z"); + + // + // Constraints + // + + // capacity of arcs + foreach(int i in ARCS) { + solver.Add(flow[arcs[i,0], arcs[i,1]] <= cap[i]); + } + + // inflows == outflows + foreach(int i in NODES) { + var s1 = (from k in ARCS + where arcs[k,1] == i + select flow[arcs[k,0], arcs[k,1]] + ).ToArray().Sum(); + + var s2 = (from k in ARCS + where arcs[k,0] == i + select flow[arcs[k,0], arcs[k,1]] + ).ToArray().Sum(); + + solver.Add(s1 == s2); + + } + + // Sanity check: just arcs with connections can have a flow. + foreach(int i in NODES) { + foreach(int j in NODES) { + if (mat[i,j] == 0) { + solver.Add(flow[i,j] == 0); + } + } + } + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(flow.Flatten(), + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MAX_VALUE); + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + foreach(int i in NODES) { + foreach(int j in NODES) { + Console.Write(flow[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/minesweeper.cs b/examples/dotnet/csharp/minesweeper.cs new file mode 100644 index 0000000000..2f51764932 --- /dev/null +++ b/examples/dotnet/csharp/minesweeper.cs @@ -0,0 +1,232 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Minesweeper +{ + + static int X = -1; + + // + // Default problem. + // It has 4 solutions. + // + static int default_r = 8; + static int default_c = 8; + static int[,] default_game = {{2, 3, X, 2, 2, X, 2, 1}, + {X, X, 4, X, X, 4, X, 2}, + {X, X, X, X, X, X, 4, X}, + {X, 5, X, 6, X, X, X, 2}, + {2, X, X, X, 5, 5, X, 2}, + {1, 3, 4, X, X, X, 4, X}, + {0, 1, X, 4, X, X, X, 3}, + {0, 1, 2, X, 2, 3, X, 2}}; + + // for the actual problem + static int r; + static int c; + static int[,] game; + + + /** + * + * Solves the Minesweeper problems. + * + * See http://www.hakank.org/google_or_tools/minesweeper.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Minesweeper"); + + // + // data + // + int[] S = {-1, 0, 1}; + + Console.WriteLine("Problem:"); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] > X) { + Console.Write(game[i,j] + " "); + } else { + Console.Write("X "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] mines = solver.MakeIntVarMatrix(r, c, 0, 1, "mines"); + // for branching + IntVar[] mines_flat = mines.Flatten(); + + // + // Constraints + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] >= 0) { + solver.Add( mines[i,j] == 0); + + // this cell is the sum of all its neighbours + var tmp = from a in S from b in S where + i + a >= 0 && + j + b >= 0 && + i + a < r && + j + b < c + select(mines[i+a,j+b]); + + solver.Add(tmp.ToArray().Sum() == game[i,j]); + + } + + if (game[i,j] > X) { + // This cell cannot be a mine since it + // has some value assigned to it + solver.Add(mines[i,j] == 0); + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(mines_flat, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++){ + Console.Write("{0} ", mines[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + /** + * + * Reads a minesweeper file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows + * number of columns + * < + * row number of neighbours lines... + * > + * + * 0..8 means number of neighbours, "." mean unknown (may be a mine) + * + * Example (from minesweeper0.txt) + * # Problem from Gecode/examples/minesweeper.cc problem 0 + * 6 + * 6 + * ..2.3. + * 2..... + * ..24.3 + * 1.34.. + * .....3 + * .3.3.. + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + r = Convert.ToInt32(str); // number of rows + } else if (lineCount == 1) { + c = Convert.ToInt32(str); // number of columns + game = new int[r,c]; + } else { + // the problem matrix + String[] row = Regex.Split(str, ""); + for(int j = 1; j <= c; j++) { + String s = row[j]; + if (s.Equals(".")) { + game[lineCount-2,j-1] = -1; + } else { + game[lineCount-2,j-1] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + game = default_game; + r = default_r; + c = default_c; + } + + Solve(); + } +} diff --git a/examples/dotnet/csharp/mr_smith.cs b/examples/dotnet/csharp/mr_smith.cs new file mode 100644 index 0000000000..16f32f1bb0 --- /dev/null +++ b/examples/dotnet/csharp/mr_smith.cs @@ -0,0 +1,136 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class MrSmith +{ + /** + * + * Mr Smith problem. + * + * 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 + * + * + * Also see http://www.hakank.org/or-tools/mr_smith.py + * + */ + private static void Solve() + { + Solver solver = new Solver("MrSmith"); + + // + // Data + // + int n = 5; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + IntVar Mr_Smith = x[0]; + IntVar Mrs_Smith = x[1]; + IntVar Matt = x[2]; + IntVar John = x[3]; + IntVar Tim = x[4]; + + + // + // 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); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\n"); + Console.WriteLine("Mr Smith : {0}", Mr_Smith.Value()); + Console.WriteLine("Mrs Smith: {0}", Mrs_Smith.Value()); + Console.WriteLine("Matt : {0}", Matt.Value()); + Console.WriteLine("John : {0}", John.Value()); + Console.WriteLine("Tim : {0}", Tim.Value()); + + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/nontransitive_dice.cs b/examples/dotnet/csharp/nontransitive_dice.cs new file mode 100644 index 0000000000..a4284496f0 --- /dev/null +++ b/examples/dotnet/csharp/nontransitive_dice.cs @@ -0,0 +1,210 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class NonTransitiveDice +{ + + /** + * + * Nontransitive dice. + * + * From + * http://en.wikipedia.org/wiki/Nontransitive_dice + * """ + * A set of nontransitive dice is a set of dice for which the relation + * 'is more likely to roll a higher number' is not transitive. See also + * intransitivity. + * + * This situation is similar to that in the game Rock, Paper, Scissors, + * in which each element has an advantage over one choice and a + * disadvantage to the other. + * """ + * + * Also see http://www.hakank.org/or-tools/nontransitive_dice.py + * + * + */ + private static void Solve(int m=3, int n=6, int minimize_val=0) + { + + Solver solver = new Solver("Nontransitive_dice"); + + Console.WriteLine("Number of dice: {0}", m); + Console.WriteLine("Number of sides: {0}", n); + Console.WriteLine("minimize_val: {0}\n", minimize_val); + + // + // Decision variables + // + + // The dice + IntVar[,] dice = solver.MakeIntVarMatrix(m, n, 1, n*2, "dice"); + IntVar[] dice_flat = dice.Flatten(); + + // For comparison (probability) + IntVar[,] comp = solver.MakeIntVarMatrix(m, 2, 0, n*n, "dice"); + IntVar[] comp_flat = comp.Flatten(); + + // For branching + IntVar[] all = dice_flat.Concat(comp_flat).ToArray(); + + // The following variables are for summaries or objectives + IntVar[] gap = solver.MakeIntVarArray(m, 0, n*n, "gap"); + IntVar gap_sum = gap.Sum().Var(); + + IntVar max_val = dice_flat.Max().Var(); + IntVar max_win = comp_flat.Max().Var(); + + // number of occurrences of each value of the dice + IntVar[] counts = solver.MakeIntVarArray(n*2+1, 0, n*m, "counts"); + + + // + // Constraints + // + + // Number of occurrences for each number + solver.Add(dice_flat.Distribute(counts)); + + // Order of the number of each die, lowest first + for(int i = 0; i < m; i++) { + for(int j = 0; j < n-1; j++) { + solver.Add(dice[i,j] <= dice[i,j+1]); + } + } + + // Nontransitivity + for(int i = 0; i < m; i++) { + solver.Add(comp[i,0] > comp[i,1]); + } + + // Probability gap + for(int i = 0; i < m; i++) { + solver.Add(gap[i] == comp[i,0] - comp[i,1]); + solver.Add(gap[i] > 0); + } + + // And now we roll... + // comp[] is the number of wins for [A vs B, B vs A] + for(int d = 0; d < m; d++) { + IntVar sum1 = ( from r1 in Enumerable.Range(0, n) + from r2 in Enumerable.Range(0, n) + select (dice[d % m, r1] > dice[(d+1) % m, r2]) + ).ToArray().Sum().Var(); + + solver.Add(comp[d%m,0] == sum1); + + IntVar sum2 = ( from r1 in Enumerable.Range(0, n) + from r2 in Enumerable.Range(0, n) + select (dice[(d+1) % m, r1] > dice[d % m, r2]) + ).ToArray().Sum().Var(); + + solver.Add(comp[d%m,1] == sum2); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MIN_VALUE); + + if (minimize_val > 0) { + Console.WriteLine("Minimizing max_val"); + + OptimizeVar obj = max_val.Minimize(1); + + // Other experiments: + // OptimizeVar obj = max_win.Maximize(1); + // OptimizeVar obj = gap_sum.Maximize(1); + + solver.NewSearch(db, obj); + + } else { + solver.NewSearch(db); + } + + while (solver.NextSolution()) { + Console.WriteLine("gap_sum: {0}", gap_sum.Value()); + Console.WriteLine("gap: {0}", (from i in Enumerable.Range(0, m) + select gap[i].Value().ToString() + ).ToArray() + ); + Console.WriteLine("max_val: {0}", max_val.Value()); + Console.WriteLine("max_win: {0}", max_win.Value()); + Console.WriteLine("dice:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + Console.Write(dice[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("comp:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < 2; j++) { + Console.Write(comp[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("counts:"); + for(int i = 1; i < n*2+1; i++) { + int c = (int)counts[i].Value(); + if (c > 0) { + Console.Write("{0}({1}) ", i, c); + } + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int m = 3; // number of dice + int n = 6; // number of sides of each die + int minimize_val = 0; // minimizing max_max (0: no, 1: yes) + + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + minimize_val = Convert.ToInt32(args[2]); + } + + Solve(m, n, minimize_val); + } +} diff --git a/examples/dotnet/csharp/nqueens.cs b/examples/dotnet/csharp/nqueens.cs new file mode 100644 index 0000000000..ff14865aa0 --- /dev/null +++ b/examples/dotnet/csharp/nqueens.cs @@ -0,0 +1,119 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class NQueens +{ + /** + * + * Solves the N-Queens problem. + * + * Syntax: nqueens.exe n num print + * where + * n : size of board + * num : number of solutions to calculate + * print: print the results (if > 0) + * + */ + private static void Solve(int n=8, int num=0, int print=1) + { + Solver solver = new Solver("N-Queens"); + + // + // Decision variables + // + IntVar[] q = solver.MakeIntVarArray(n, 0, n-1, "q"); + + + // + // Constraints + // + solver.Add(q.AllDifferent()); + + IntVar[] q1 = new IntVar[n]; + IntVar[] q2 = new IntVar[n]; + for(int i = 0; i < n; i++) { + q1[i] = (q[i] + i).Var(); + q2[i] = (q[i] - i).Var(); + } + solver.Add(q1.AllDifferent()); + solver.Add(q2.AllDifferent()); + + // Alternative version: it works as well but are not that clear + /* + solver.Add((from i in Enumerable.Range(0, n) + select (q[i] + i).Var()).ToArray().AllDifferent()); + + solver.Add((from i in Enumerable.Range(0, n) + select (q[i] - i).Var()).ToArray().AllDifferent()); + */ + + // + // Search + // + DecisionBuilder db = solver.MakePhase(q, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + int c = 0; + while (solver.NextSolution()) { + if (print > 0) { + for(int i = 0; i < n; i++) { + Console.Write("{0} ", q[i].Value()); + } + + Console.WriteLine(); + } + c++; + if (num > 0 && c >= num) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 8; + int num = 0; + int print = 1; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num = Convert.ToInt32(args[1]); + } + if (args.Length > 2) { + print = Convert.ToInt32(args[2]); + } + + Console.WriteLine("n: {0} num: {1} print: {2}", n, num, print); + + Solve(n, num, print); + } +} diff --git a/examples/dotnet/csharp/nurse_rostering_regular.cs b/examples/dotnet/csharp/nurse_rostering_regular.cs new file mode 100644 index 0000000000..35654a186a --- /dev/null +++ b/examples/dotnet/csharp/nurse_rostering_regular.cs @@ -0,0 +1,323 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class NurseRostering +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i])*S)+(x[i]-1))); + + } + + } + + + /** + * + * Nurse rostering + * + * This is a simple nurse rostering model using a DFA and + * my decomposition of regular constraint. + * + * The DFA is from MiniZinc Tutorial, Nurse Rostering example: + * - one day off every 4 days + * - no 3 nights in a row. + * + * Also see http://www.hakank.org/or-tools/nurse_rostering.py + * + */ + private static void Solve() + { + Solver solver = new Solver("NurseRostering"); + + // + // Data + // + + // Note: If you change num_nurses or num_days, + // please also change the constraints + // on nurse_stat and/or day_stat. + int num_nurses = 7; + int num_days = 14; + + // Note: I had to add a dummy shift. + int dummy_shift = 0; + int day_shift = 1; + int night_shift = 2; + int off_shift = 3; + int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; + int[] valid_shifts = {day_shift, night_shift, off_shift}; + + // the DFA (for regular) + int n_states = 6; + int input_max = 3; + int initial_state = 1; // 0 is for the failing state + int[] accepting_states = {1,2,3,4,5,6}; + + int[,] transition_fn = { + // d,n,o + {2,3,1}, // state 1 + {4,4,1}, // state 2 + {4,5,1}, // state 3 + {6,6,1}, // state 4 + {6,0,1}, // state 5 + {0,0,1} // state 6 + }; + + string[] days = {"d","n","o"}; // for presentation + + // + // Decision variables + // + + // For regular + IntVar[,] x = + solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); + IntVar[] x_flat = x.Flatten(); + + // summary of the nurses + IntVar[] nurse_stat = + solver.MakeIntVarArray(num_nurses, 0, num_days, "nurse_stat"); + + // summary of the shifts per day + int num_shifts = shifts.Length; + IntVar[,] day_stat = new IntVar[num_days, num_shifts]; + for(int i = 0; i < num_days; i++) { + for(int j = 0; j < num_shifts; j++) { + day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); + } + } + + + // + // Constraints + // + for(int i = 0; i < num_nurses; i++) { + IntVar[] reg_input = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + reg_input[j] = x[i,j]; + } + MyRegular(solver, reg_input, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + } + + // + // Statistics and constraints for each nurse + // + for(int i = 0; i < num_nurses; i++) { + + // Number of worked days (either day or night shift) + IntVar[] b = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + b[j] = ((x[i,j] == day_shift) + (x[i,j] == night_shift)).Var(); + } + solver.Add(b.Sum() == nurse_stat[i]); + + // Each nurse must work between 7 and 10 + // days/nights during this period + solver.Add(nurse_stat[i] >= 7); + solver.Add(nurse_stat[i] <= 10); + + } + + + // + // Statistics and constraints for each day + // + for(int j = 0; j < num_days; j++) { + for(int t = 0; t < num_shifts; t++) { + IntVar[] b = new IntVar[num_nurses]; + for(int i = 0; i < num_nurses; i++) { + b[i] = x[i,j] == t; + } + solver.Add(b.Sum() == day_stat[j,t]); + } + + // + // Some constraints for each day: + // + // Note: We have a strict requirements of + // the number of shifts. + // Using atleast constraints is harder + // in this model. + // + if (j % 7 == 5 || j % 7 == 6) { + // special constraints for the weekends + solver.Add(day_stat[j,day_shift] == 2); + solver.Add(day_stat[j,night_shift] == 1); + solver.Add(day_stat[j,off_shift] == 4 ); + } else { + // for workdays: + + // - exactly 3 on day shift + solver.Add(day_stat[j,day_shift] == 3); + // - exactly 2 on night + solver.Add(day_stat[j,night_shift] == 2); + // - exactly 2 off duty + solver.Add(day_stat[j,off_shift] == 2 ); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + for(int i = 0; i < num_nurses; i++) { + Console.Write("Nurse #{0,-2}: ", i); + var occ = new Dictionary(); + for(int j = 0; j < num_days; j++) { + int v = (int)x[i,j].Value()-1; + if (!occ.ContainsKey(v)) { + occ[v] = 0; + } + occ[v]++; + Console.Write(days[v] + " "); + } + + Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); + foreach(int s in valid_shifts) { + int v = 0; + if (occ.ContainsKey(s-1)) { + v = occ[s-1]; + } + Console.Write(" {0}:{1}", days[s-1], v); + } + Console.WriteLine(); + + } + Console.WriteLine(); + + Console.WriteLine("Statistics per day:\nDay d n o"); + for(int j = 0; j < num_days; j++) { + Console.Write("Day #{0,2}: ", j); + foreach(int t in valid_shifts) { + Console.Write(day_stat[j,t].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + // We just show 2 solutions + if (num_solutions > 1) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/nurse_rostering_transition.cs b/examples/dotnet/csharp/nurse_rostering_transition.cs new file mode 100644 index 0000000000..0711de4d00 --- /dev/null +++ b/examples/dotnet/csharp/nurse_rostering_transition.cs @@ -0,0 +1,286 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class NurseRostering +{ + + /** + * + * Nurse rostering + * + * This is a simple nurse rostering model using a DFA and + * the built-in TransitionConstraint. + * + * The DFA is from MiniZinc Tutorial, Nurse Rostering example: + * - one day off every 4 days + * - no 3 nights in a row. + * + * Also see: + * - http://www.hakank.org/or-tools/nurse_rostering.py + * - http://www.hakank.org/or-tools/nurse_rostering_regular.cs + * which use (a decomposition of) regular constraint + * + */ + private static void Solve(int nurse_multiplier, int week_multiplier) + { + Console.WriteLine("Starting Nurse Rostering"); + Console.WriteLine(" - {0} teams of 7 nurses", nurse_multiplier); + Console.WriteLine(" - {0} blocks of 14 days", week_multiplier); + + Solver solver = new Solver("NurseRostering"); + + // + // Data + // + + // Note: If you change num_nurses or num_days, + // please also change the constraints + // on nurse_stat and/or day_stat. + int num_nurses = 7 * nurse_multiplier; + int num_days = 14 * week_multiplier; + + // Note: I had to add a dummy shift. + int dummy_shift = 0; + int day_shift = 1; + int night_shift = 2; + int off_shift = 3; + int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; + int[] valid_shifts = {day_shift, night_shift, off_shift}; + + // the DFA (for regular) + int initial_state = 1; + int[] accepting_states = {1,2,3,4,5,6}; + + /* + // This is the transition function + // used in nurse_rostering_regular.cs + int[,] transition_fn = { + // d,n,o + {2,3,1}, // state 1 + {4,4,1}, // state 2 + {4,5,1}, // state 3 + {6,6,1}, // state 4 + {6,0,1}, // state 5 + {0,0,1} // state 6 + }; + */ + + // For TransitionConstraint + IntTupleSet transition_tuples = new IntTupleSet(3); + // state, input, next state + transition_tuples.InsertAll(new int[,] { {1,1,2}, + {1,2,3}, + {1,3,1}, + {2,1,4}, + {2,2,4}, + {2,3,1}, + {3,1,4}, + {3,2,5}, + {3,3,1}, + {4,1,6}, + {4,2,6}, + {4,3,1}, + {5,1,6}, + {5,3,1}, + {6,3,1} }); + + string[] days = {"d","n","o"}; // for presentation + + // + // Decision variables + // + + // + // For TransitionConstraint + // + IntVar[,] x = + solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // summary of the nurses + // + IntVar[] nurse_stat = new IntVar[num_nurses]; + + // + // summary of the shifts per day + // + int num_shifts = shifts.Length; + IntVar[,] day_stat = new IntVar[num_days, num_shifts]; + for(int i = 0; i < num_days; i++) { + for(int j = 0; j < num_shifts; j++) { + day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); + } + } + + // + // Constraints + // + for(int i = 0; i < num_nurses; i++) { + IntVar[] reg_input = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + reg_input[j] = x[i,j]; + } + + solver.Add(reg_input.Transition(transition_tuples, + initial_state, + accepting_states)); + } + + // + // Statistics and constraints for each nurse + // + for(int nurse = 0; nurse < num_nurses; nurse++) { + + // Number of worked days (either day or night shift) + IntVar[] nurse_days = new IntVar[num_days]; + for(int day = 0; day < num_days; day++) { + nurse_days[day] = + x[nurse, day].IsMember(new int[] { day_shift, night_shift }); + } + nurse_stat[nurse] = nurse_days.Sum().Var(); + + // Each nurse must work between 7 and 10 + // days/nights during this period + solver.Add(nurse_stat[nurse] >= 7 * week_multiplier / nurse_multiplier); + solver.Add(nurse_stat[nurse] <= 10 * week_multiplier / nurse_multiplier); + + } + + // + // Statistics and constraints for each day + // + for(int day = 0; day < num_days; day++) { + IntVar[] nurses = new IntVar[num_nurses]; + for(int nurse = 0; nurse < num_nurses; nurse++) { + nurses[nurse] = x[nurse, day]; + } + IntVar[] stats = new IntVar[num_shifts]; + for (int shift = 0; shift < num_shifts; ++shift) + { + stats[shift] = day_stat[day, shift]; + } + solver.Add(nurses.Distribute(stats)); + + // + // Some constraints for each day: + // + // Note: We have a strict requirements of + // the number of shifts. + // Using atleast constraints is harder + // in this model. + // + if (day % 7 == 5 || day % 7 == 6) { + // special constraints for the weekends + solver.Add(day_stat[day, day_shift] == 2 * nurse_multiplier); + solver.Add(day_stat[day, night_shift] == nurse_multiplier); + solver.Add(day_stat[day, off_shift] == 4 * nurse_multiplier); + } else { + // for workdays: + + // - exactly 3 on day shift + solver.Add(day_stat[day, day_shift] == 3 * nurse_multiplier); + // - exactly 2 on night + solver.Add(day_stat[day, night_shift] == 2 * nurse_multiplier); + // - exactly 2 off duty + solver.Add(day_stat[day, off_shift] == 2 * nurse_multiplier); + } + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + SearchMonitor log = solver.MakeSearchLog(1000000); + + solver.NewSearch(db, log); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + for(int i = 0; i < num_nurses; i++) { + Console.Write("Nurse #{0,-2}: ", i); + var occ = new Dictionary(); + for(int j = 0; j < num_days; j++) { + int v = (int)x[i,j].Value()-1; + if (!occ.ContainsKey(v)) { + occ[v] = 0; + } + occ[v]++; + Console.Write(days[v] + " "); + } + + Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); + foreach(int s in valid_shifts) { + int v = 0; + if (occ.ContainsKey(s-1)) { + v = occ[s-1]; + } + Console.Write(" {0}:{1}", days[s-1], v); + } + Console.WriteLine(); + + } + Console.WriteLine(); + + Console.WriteLine("Statistics per day:\nDay d n o"); + for(int j = 0; j < num_days; j++) { + Console.Write("Day #{0,2}: ", j); + foreach(int t in valid_shifts) { + Console.Write(day_stat[j,t].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + // We just show 2 solutions + if (num_solutions > 1) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int nurse_multiplier = 1; + int week_multiplier = 1; + if (args.Length > 0) { + nurse_multiplier = Convert.ToInt32(args[0]); + } + if (args.Length > 1) { + week_multiplier = Convert.ToInt32(args[1]); + } + + Solve(nurse_multiplier, week_multiplier); + } +} diff --git a/examples/dotnet/csharp/nurses_sat.cs b/examples/dotnet/csharp/nurses_sat.cs new file mode 100644 index 0000000000..cdeac6a057 --- /dev/null +++ b/examples/dotnet/csharp/nurses_sat.cs @@ -0,0 +1,219 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class NurseSolutionObserver : CpSolverSolutionCallback +{ + public NurseSolutionObserver(IntVar[,,] shifts, int num_nurses, int num_days, + int num_shifts, HashSet to_print) { + shifts_ = shifts; + num_nurses_ = num_nurses; + num_days_ = num_days; + num_shifts_ = num_shifts; + to_print_ = to_print; + } + + public override void OnSolutionCallback() + { + solution_count_++; + if (to_print_.Contains(solution_count_)) + { + Console.WriteLine( + String.Format("Solution #{0}: time = {1:.02} s", + solution_count_, WallTime())); + for (int d = 0; d < num_days_; ++d) + { + Console.WriteLine(String.Format("Day #{0}", d)); + for (int n = 0; n < num_nurses_; ++n) + { + for (int s = 0; s < num_shifts_; ++s) + { + if (BooleanValue(shifts_[n, d, s])) + { + Console.WriteLine( + String.Format(" Nurse #{0} is working shift #{1}", n, s)); + } + } + } + } + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[,,] shifts_; + private int num_nurses_; + private int num_days_; + private int num_shifts_; + private HashSet to_print_; +} + +public class NursesSat +{ + static void Solve() + { + // Data. + int num_nurses = 4; + // Nurse assigned to shift 0 means not working that day. + int num_shifts = 4; + int num_days = 7; + + var all_nurses = Enumerable.Range(0, num_nurses); + var all_shifts = Enumerable.Range(0, num_shifts); + var all_working_shifts = Enumerable.Range(1, num_shifts - 1); + var all_days = Enumerable.Range(0, num_days); + + // Creates the model. + CpModel model = new CpModel(); + + // Creates shift variables. + // shift[n, d, s]: nurse "n" works shift "s" on day "d". + IntVar[,,] shift = new IntVar[num_nurses, num_days, num_shifts]; + foreach (int n in all_nurses) + { + foreach (int d in all_days) + { + foreach (int s in all_shifts) + { + shift[n, d, s] = + model.NewBoolVar(String.Format("shift_n{0}d{1}s{2}", n, d, s)); + } + } + } + + // Makes assignments different on each day, that is each shift is + // assigned at most one nurse. As we have the same number of + // nurses and shifts, then each day, each shift is assigned to + // exactly one nurse. + foreach (int d in all_days) + { + foreach (int s in all_shifts) + { + IntVar[] tmp = new IntVar[num_nurses]; + foreach (int n in all_nurses) + { + tmp[n] = shift[n, d, s]; + } + model.Add(tmp.Sum() == 1); + } + } + + // Nurses do 1 shift per day. + foreach (int n in all_nurses) + { + foreach (int d in all_days) + { + IntVar[] tmp = new IntVar[num_shifts]; + foreach (int s in all_shifts) + { + tmp[s] = shift[n, d, s]; + } + model.Add(tmp.Sum() == 1); + } + } + + // Each nurse works 5 or 6 days in a week. + // That is each nurse works shift 0 at most 2 times. + foreach (int n in all_nurses) + { + IntVar[] tmp = new IntVar[num_days]; + foreach (int d in all_days) + { + tmp[d] = shift[n, d, 0]; + } + model.Add(1 <= tmp.Sum() <= 2); + } + + // works_shift[(n, s)] is 1 if nurse n works shift s at least one day in + // the week. + IntVar[,] works_shift = new IntVar[num_nurses, num_shifts]; + foreach (int n in all_nurses) + { + foreach (int s in all_shifts) + { + works_shift[n, s] = + model.NewBoolVar(String.Format("works_shift_n{0}s{1}", n, s)); + IntVar[] tmp = new IntVar[num_days]; + foreach (int d in all_days) + { + tmp[d] = shift[n, d, s]; + } + model.AddMaxEquality(works_shift[n, s], tmp); + } + } + + // For each working shift, at most 2 nurses are assigned to that shift + // during the week. + foreach (int s in all_working_shifts) + { + IntVar[] tmp = new IntVar[num_nurses]; + foreach (int n in all_nurses) + { + tmp[n] = works_shift[n, s]; + } + model.Add(tmp.Sum() <= 2); + } + + // If a nurse works shifts 2 or 3 on, she must also work that + // shift the previous day or the following day. This means that + // on a given day and shift, either she does not work that shift + // on that day, or she works that shift on the day before, or the + // day after. + foreach (int n in all_nurses) + { + for (int s = 2; s <= 3; ++s) + { + foreach (int d in all_days) + { + int yesterday = d == 0 ? num_days - 1 : d - 1; + int tomorrow = d == num_days - 1 ? 0 : d + 1; + model.AddBoolOr(new ILiteral[] { shift[n, yesterday, s], + shift[n, d, s].Not(), + shift[n, tomorrow, s] } ); + } + } + } + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + // Display a few solutions picked at random. + HashSet to_print = new HashSet(); + to_print.Add(859); + to_print.Add(2034); + to_print.Add(5091); + to_print.Add(7003); + NurseSolutionObserver cb = new NurseSolutionObserver( + shift, num_nurses, num_days, num_shifts, to_print); + CpSolverStatus status = solver.SearchAllSolutions(model, cb); + + // Statistics. + Console.WriteLine("Statistics"); + Console.WriteLine(String.Format(" - solve status : {0}", status)); + Console.WriteLine(" - conflicts : " + solver.NumConflicts()); + Console.WriteLine(" - branches : " + solver.NumBranches()); + Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); + Console.WriteLine(" - #solutions : " + cb.SolutionCount()); + } + + static void Main() { + Solve(); + } +} diff --git a/examples/dotnet/csharp/olympic.cs b/examples/dotnet/csharp/olympic.cs new file mode 100644 index 0000000000..9e1df76e4b --- /dev/null +++ b/examples/dotnet/csharp/olympic.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Olympic +{ + + public static void minus(Solver solver, + IntVar x, + IntVar y, + IntVar z) + { + solver.Add(z == (x - y).Abs()); + } + + + + /** + * + * Olympic puzzle. + * + * 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 + * """ + * + * Also see http://www.hakank.org/or-tools/olympic.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Olympic"); + + // + // Data + // + int n = 10; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + IntVar X1 = x[0]; + IntVar X2 = x[1]; + IntVar X3 = x[2]; + IntVar X4 = x[3]; + IntVar X5 = x[4]; + IntVar X6 = x[5]; + IntVar X7 = x[6]; + IntVar X8 = x[7]; + IntVar X9 = x[8]; + IntVar X10 = x[9]; + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + 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); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0,2} ", x[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/organize_day.cs b/examples/dotnet/csharp/organize_day.cs new file mode 100644 index 0000000000..f41c4647d5 --- /dev/null +++ b/examples/dotnet/csharp/organize_day.cs @@ -0,0 +1,148 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class OrganizeDay +{ + + + // + // No overlapping of tasks s1 and s2 + // + public static void NoOverlap(Solver solver, + IntVar s1, int d1, + IntVar s2, int d2) + { + solver.Add((s1 + d1 <= s2) + (s2 + d2 <= s1) == 1); + } + + + + /** + * + * + * Organizing a day. + * + * Simple scheduling problem. + * + * Problem formulation from ECLiPSe: + * Slides on (Finite Domain) Constraint Logic Programming, page 38f + * http://eclipseclp.org/reports/eclipse.ppt + * + * + * Also see http://www.hakank.org/google_or_tools/organize_day.py + * + */ + private static void Solve() + { + Solver solver = new Solver("OrganizeDay"); + + + int n = 4; + + + int work = 0; + int mail = 1; + int shop = 2; + int bank = 3; + int[] tasks = {work, mail, shop, bank}; + int[] durations = {4,1,2,1}; + + // task [i,0] must be finished before task [i,1] + int[,] before_tasks = { + {bank, shop}, + {mail, work} + }; + + // the valid times of the day + int begin = 9; + int end = 17; + + + // + // Decision variables + // + IntVar[] begins = solver.MakeIntVarArray(n, begin, end, "begins"); + IntVar[] ends = solver.MakeIntVarArray(n, begin, end, "ends"); + + // + // Constraints + // + foreach(int t in tasks) { + solver.Add(ends[t] == begins[t] + durations[t]); + } + + foreach(int i in tasks) { + foreach(int j in tasks) { + if (i < j) { + NoOverlap(solver, + begins[i], durations[i], + begins[j], durations[j]); + } + } + } + + // specific constraints + for(int t = 0; t < before_tasks.GetLength(0); t++) { + solver.Add(ends[before_tasks[t,0]] <= begins[before_tasks[t,1]]); + } + + solver.Add(begins[work] >= 11); + + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(begins, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int t in tasks) { + Console.WriteLine("Task {0}: {1,2} .. ({2}) .. {3,2}", + t, + begins[t].Value(), + durations[t], + ends[t].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/organize_day_intervals.cs b/examples/dotnet/csharp/organize_day_intervals.cs new file mode 100644 index 0000000000..00d85a9872 --- /dev/null +++ b/examples/dotnet/csharp/organize_day_intervals.cs @@ -0,0 +1,124 @@ +// Copyright 2010-2017 Google +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class OrganizeDay +{ + /** + * + * + * Organizing a day. + * + * Simple scheduling problem. + * + * Problem formulation from ECLiPSe: + * Slides on (Finite Domain) Constraint Logic Programming, page 38f + * http://eclipseclp.org/reports/eclipse.ppt + * + * + * Also see http://www.hakank.org/google_or_tools/organize_day.py + * + */ + private static void Solve() + { + Solver solver = new Solver("OrganizeDayIntervals"); + + + int n = 4; + + + int work = 0; + int mail = 1; + int shop = 2; + int bank = 3; + // the valid times of the day + int begin = 9; + int end = 17; + // tasks + int[] tasks = {work, mail, shop, bank}; + // durations + int[] durations = {4,1,2,1}; + // Arrays for interval variables. + int[] starts_max = { begin,begin,begin,begin }; + int[] ends_max = { end -4, end - 1, end - 2, end - 1 }; + + // task [i,0] must be finished before task [i,1] + int[,] before_tasks = { + {bank, shop}, + {mail, work} + }; + + + + // + // Decision variables + // + IntervalVar[] intervals = + solver.MakeFixedDurationIntervalVarArray(n, + starts_max, + ends_max, + durations, + false, + "task"); + // + // Constraints + // + DisjunctiveConstraint disjunctive = intervals.Disjunctive("Sequence"); + solver.Add(disjunctive); + + // specific constraints + for(int t = 0; t < before_tasks.GetLength(0); t++) { + int before = before_tasks[t, 0]; + int after = before_tasks[t, 1]; + solver.Add(intervals[after].StartsAfterEnd(intervals[before])); + } + + solver.Add(intervals[work].StartsAfter(11)); + + // + // Search + // + SequenceVar var = disjunctive.SequenceVar(); + SequenceVar[] seq_array = new SequenceVar[] { var }; + DecisionBuilder db = solver.MakePhase(seq_array, Solver.SEQUENCE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int t in tasks) { + Console.WriteLine(intervals[t].ToString()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/p_median.cs b/examples/dotnet/csharp/p_median.cs new file mode 100644 index 0000000000..53a2f72c0b --- /dev/null +++ b/examples/dotnet/csharp/p_median.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PMedian +{ + /** + * + * P-median problem. + * + * 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. + * """ + * + * Also see http://www.hakank.org/or-tools/p_median.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("PMedian"); + + // + // Data + // + int p = 2; + int num_customers = 4; + IEnumerable CUSTOMERS = Enumerable.Range(0, num_customers); + + int num_warehouses = 3; + IEnumerable WAREHOUSES = Enumerable.Range(0, num_warehouses); + + int[] demand = {100,80,80,70}; + int [,] distance = { + { 2, 10, 50}, + { 2, 10, 52}, + {50, 60, 3}, + {40, 60, 1} + }; + + // + // Decision variables + // + + IntVar[] open = solver.MakeIntVarArray(num_warehouses, 0, num_warehouses, "open"); + IntVar[,] ship = solver.MakeIntVarMatrix(num_customers, num_warehouses, + 0, 1, "ship"); + IntVar z = solver.MakeIntVar(0, 1000, "z"); + + + // + // Constraints + // + + solver.Add((from c in CUSTOMERS + from w in WAREHOUSES + select (demand[c]*distance[c,w]*ship[c,w]) + ).ToArray().Sum() == z); + + solver.Add(open.Sum() == p); + + foreach(int c in CUSTOMERS) { + foreach(int w in WAREHOUSES) { + solver.Add(ship[c,w] <= open[w]); + } + + solver.Add((from w in WAREHOUSES select ship[c,w]).ToArray().Sum() == 1); + } + + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(open.Concat(ship.Flatten()).ToArray(), + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + Console.Write("open:"); + foreach(int w in WAREHOUSES) { + Console.Write(open[w].Value() + " "); + } + Console.WriteLine(); + foreach(int c in CUSTOMERS) { + foreach(int w in WAREHOUSES) { + Console.Write(ship[c,w].Value()+ " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/pandigital_numbers.cs b/examples/dotnet/csharp/pandigital_numbers.cs new file mode 100644 index 0000000000..117b6b786e --- /dev/null +++ b/examples/dotnet/csharp/pandigital_numbers.cs @@ -0,0 +1,193 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PandigitalNumbers +{ + + /** + * + * toNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, + IntVar num, + int bbase) { + int len = a.Length; + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,len-i-1)).Var(); + } + return tmp.Sum() == num; + } + + + /** + * + * Pandigital numbers in Google CP Solver. + * + * From Albert H. Beiler 'Recreations in the Theory of Numbers', + * quoted from http://www.worldofnumbers.com/ninedig1.htm + * """ + * Chapter VIII : Digits - and the magic of 9 + * + * The following curious table shows how to arrange the 9 digits so that + * the product of 2 groups is equal to a number represented by the + * remaining digits. + * + * 12 x 483 = 5796 + * 42 x 138 = 5796 + * 18 x 297 = 5346 + * 27 x 198 = 5346 + * 39 x 186 = 7254 + * 48 x 159 = 7632 + * 28 x 157 = 4396 + * 4 x 1738 = 6952 + * 4 x 1963 = 7852 + * """ + * + * Also see MathWorld http://mathworld.wolfram.com/PandigitalNumber.html + * """ + * A number is said to be pandigital if it contains each of the digits + * from 0 to 9 (and whose leading digit must be nonzero). However, + * "zeroless" pandigital quantities contain the digits 1 through 9. + * Sometimes exclusivity is also required so that each digit is + * restricted to appear exactly once. + * """ + * + * Wikipedia: http://en.wikipedia.org/wiki/Pandigital_number + * + * + * Also see http://www.hakank.org/or-tools/pandigital_numbers.py + * + */ + private static void Solve(int bbase=10, int start=1, int len1=1, int len2=4) + { + + Solver solver = new Solver("PandigitalNumbers"); + + // + // Data + // + int max_d = bbase-1; + int x_len = max_d + 1 - start; + int max_num = (int)Math.Pow(bbase,4)-1; + + // + // Decision variables + // + IntVar num1 = solver.MakeIntVar(1, max_num, "num1"); + IntVar num2 = solver.MakeIntVar(1, max_num, "num2"); + IntVar res = solver.MakeIntVar(1, max_num, "res"); + + IntVar[] x = solver.MakeIntVarArray(x_len, start, max_d, "x"); + + // for labeling + IntVar[] all = new IntVar[x_len+3]; + for(int i = 0; i < x_len; i++) { + all[i] = x[i]; + } + all[x_len] = num1; + all[x_len+1] = num2; + all[x_len+2] = res; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + solver.Add(ToNum(( from i in Enumerable.Range(0, len1) + select x[i]).ToArray(), + num1, + bbase)); + + solver.Add(ToNum(( from i in Enumerable.Range(len1, len2) + select x[i]).ToArray(), + num2, + bbase)); + + solver.Add(ToNum(( from i in Enumerable.Range(len1+len2, x_len-(len1+len2)) + select x[i]).ToArray(), + res, + bbase)); + + + solver.Add(num1*num2 == res); + + // no number must start with 0 + solver.Add(x[0] > 0); + solver.Add(x[len1] > 0); + solver.Add(x[len1+len2] > 0); + + // symmetry breaking + solver.Add(num1 < num2); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("{0} * {1} = {2}", num1.Value(), num2.Value(), res.Value()); + } + + /* + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + */ + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int bbase = 10; + int start = 1; + + if(args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + } + + if(args.Length > 1) { + start = Convert.ToInt32(args[1]); + } + + int x_len = bbase - 1 + 1-start; + for(int len1 = 0; len1 <= x_len; len1++) { + for(int len2 = 0; len2 <= x_len; len2++) { + if (x_len > len1 + len2 + && len1 > 0 && len2 > 0 + ) { + Solve(bbase, start, len1, len2); + } + } + } + + } +} diff --git a/examples/dotnet/csharp/partition.cs b/examples/dotnet/csharp/partition.cs new file mode 100644 index 0000000000..3826c4ebe7 --- /dev/null +++ b/examples/dotnet/csharp/partition.cs @@ -0,0 +1,130 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Partition +{ + + /** + * + * This is a port of Charles Prud'homme's Java model + * Partition.java + * """ + * Partition n numbers into two groups, so that + * - the sum of the first group equals the sum of the second, + * - and the sum of the squares of the first group equals the sum of + * the squares of the second + * """ + * + */ + private static void Solve(int m) + { + + Solver solver = new Solver("Partition"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(m, 1, 2 * m, "x"); + IntVar[] y = solver.MakeIntVarArray(m, 1, 2 * m, "y"); + + + // + // Constraints + // + // break symmetries + for (int i = 0; i < m - 1; i++) { + solver.Add(x[i] < x[i + 1]); + solver.Add(y[i] < y[i + 1]); + } + solver.Add(x[0] < y[0]); + + IntVar[] xy = new IntVar[2 * m]; + for (int i = m - 1; i >= 0; i--) { + xy[i] = x[i]; + xy[m + i] = y[i]; + } + + solver.Add(xy.AllDifferent()); + + int[] coeffs = new int[2 * m]; + for (int i = m - 1; i >= 0; i--) { + coeffs[i] = 1; + coeffs[m + i] = -1; + } + solver.Add(xy.ScalProd(coeffs) == 0); + + IntVar[] sxy, sx, sy; + sxy = new IntVar[2 * m]; + sx = new IntVar[m]; + sy = new IntVar[m]; + for (int i = m - 1; i >= 0; i--) { + sx[i] = x[i].Square().Var(); + sxy[i] = sx[i]; + sy[i] = y[i].Square().Var(); + sxy[m + i] = sy[i]; + } + solver.Add(sxy.ScalProd(coeffs) == 0); + + solver.Add(x.Sum() == 2 * m * (2 * m + 1) / 4); + solver.Add(y.Sum() == 2 * m * (2 * m + 1) / 4); + solver.Add(sx.Sum() == 2 * m * (2 * m + 1) * (4 * m + 1) / 12); + solver.Add(sy.Sum() == 2 * m * (2 * m + 1) * (4 * m + 1) / 12); + + // + // Search + // + DecisionBuilder db = solver.MakeDefaultPhase(xy); + + SearchMonitor log = solver.MakeSearchLog(10000); + solver.NewSearch(db, log); + + while (solver.NextSolution()) { + for(int i = 0; i < m; i++) { + Console.Write("[" + xy[i].Value() + "] "); + } + Console.WriteLine(); + for(int i = 0; i < m; i++) { + Console.Write("[" + xy[m+i].Value() + "] "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int m = 32; + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + Solve(m); + } +} diff --git a/examples/dotnet/csharp/perfect_square_sequence.cs b/examples/dotnet/csharp/perfect_square_sequence.cs new file mode 100644 index 0000000000..d51bc9a592 --- /dev/null +++ b/examples/dotnet/csharp/perfect_square_sequence.cs @@ -0,0 +1,144 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class PerfectSquareSequence +{ + + /** + * + * Perfect square sequence. + * + * From 'Fun with num3ers' + * "Sequence" + * http://benvitale-funwithnum3ers.blogspot.com/2010/11/sequence.html + * """ + * If we take the numbers from 1 to 15 + * (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + * and rearrange them in such an order that any two consecutive + * numbers in the sequence add up to a perfect square, we get, + * + * 8 1 15 10 6 3 13 12 4 5 11 14 2 7 9 + * 9 16 25 16 9 16 25 16 9 16 25 16 9 16 + * + * + * I ask the readers the following: + * + * Can you take the numbers from 1 to 25 to produce such an arrangement? + * How about the numbers from 1 to 100? + * """ + * + * Via http://wildaboutmath.com/2010/11/26/wild-about-math-bloggers-111910 + * + * + * Also see http://www.hakank.org/or-tools/perfect_square_sequence.py + * + */ + private static int Solve(int n = 15, int print_solutions=1, int show_num_sols=0) + { + + Solver solver = new Solver("PerfectSquareSequence"); + + IEnumerable RANGE = Enumerable.Range(0, n); + + // create the table of possible squares + int[] squares = new int[n-1]; + for(int i = 1; i < n; i++) { + squares[i-1] = i*i; + } + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + for(int i = 1; i < n; i++) { + solver.Add((x[i-1]+x[i]).Member(squares)); + } + + // symmetry breaking + solver.Add(x[0] < x[n-1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + if (print_solutions > 0) { + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + if (show_num_sols > 0 && num_solutions >= show_num_sols) { + break; + } + } + + if (print_solutions > 0) { + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + } + + solver.EndSearch(); + + return num_solutions; + + } + + public static void Main(String[] args) + { + int n = 15; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (n == 0) { + for(int i = 2; i < 100; i++) { + int num_solutions = Solve(i, 0, 0); + Console.WriteLine("{0}: {1} solution(s)", i, num_solutions); + } + + } else { + int num_solutions = Solve(n); + Console.WriteLine("{0}: {1} solution(s)", n, num_solutions); + } + } +} diff --git a/examples/dotnet/csharp/photo_problem.cs b/examples/dotnet/csharp/photo_problem.cs new file mode 100644 index 0000000000..4137f6c32b --- /dev/null +++ b/examples/dotnet/csharp/photo_problem.cs @@ -0,0 +1,178 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PhotoProblem +{ + + + + /** + * + * Photo problem. + * + * Problem statement from Mozart/Oz tutorial: + * http://www.mozart-oz.org/home/doc/fdt/node37.html#section.reified.photo + * """ + * Betty, Chris, Donald, Fred, Gary, Mary, and Paul want to align in one + * row for taking a photo. Some of them have preferences next to whom + * they want to stand: + * + * 1. Betty wants to stand next to Gary and Mary. + * 2. Chris wants to stand next to Betty and Gary. + * 3. Fred wants to stand next to Mary and Donald. + * 4. Paul wants to stand next to Fred and Donald. + * + * Obviously, it is impossible to satisfy all preferences. Can you find + * an alignment that maximizes the number of satisfied preferences? + * """ + * + * Oz solution: + * 6 # alignment(betty:5 chris:6 donald:1 fred:3 gary:7 mary:4 paul:2) + * [5, 6, 1, 3, 7, 4, 2] + * + * + * Also see http://www.hakank.org/or-tools/photo_problem.py + * + */ + private static void Solve(int show_all_max=0) + { + + Solver solver = new Solver("PhotoProblem"); + + // + // Data + // + String[] persons = {"Betty", "Chris", "Donald", "Fred", "Gary", "Mary", "Paul"}; + int n = persons.Length; + IEnumerable RANGE = Enumerable.Range(0, n); + + int[,] preferences = { + // 0 1 2 3 4 5 6 + // B C D F G M P + { 0,0,0,0,1,1,0 }, // Betty 0 + { 1,0,0,0,1,0,0 }, // Chris 1 + { 0,0,0,0,0,0,0 }, // Donald 2 + { 0,0,1,0,0,1,0 }, // Fred 3 + { 0,0,0,0,0,0,0 }, // Gary 4 + { 0,0,0,0,0,0,0 }, // Mary 5 + { 0,0,1,1,0,0,0 } // Paul 6 + }; + + Console.WriteLine("Preferences:"); + Console.WriteLine("1. Betty wants to stand next to Gary and Mary."); + Console.WriteLine("2. Chris wants to stand next to Betty and Gary."); + Console.WriteLine("3. Fred wants to stand next to Mary and Donald."); + Console.WriteLine("4. Paul wants to stand next to Fred and Donald.\n"); + + + // + // Decision variables + // + IntVar[] positions = solver.MakeIntVarArray(n, 0, n-1, "positions"); + // successful preferences (to Maximize) + IntVar z = solver.MakeIntVar(0, n*n, "z"); + + // + // Constraints + // + solver.Add(positions.AllDifferent()); + + // calculate all the successful preferences + solver.Add( ( from i in RANGE + from j in RANGE + where preferences[i,j] == 1 + select (positions[i] - positions[j]).Abs() == 1 + ).ToArray().Sum() == z); + + // + // Symmetry breaking (from the Oz page): + // Fred is somewhere left of Betty + solver.Add(positions[3] < positions[0]); + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + if (show_all_max > 0) { + Console.WriteLine("Showing all maximum solutions (z == 6).\n"); + solver.Add(z == 6); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(positions, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + int[] p = new int[n]; + Console.Write("p: "); + for(int i = 0; i < n; i++) { + p[i] = (int)positions[i].Value(); + Console.Write(p[i] + " "); + } + Console.WriteLine(); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (p[j] == i) { + Console.Write(persons[j] + " "); + } + } + } + Console.WriteLine(); + Console.WriteLine("Successful preferences:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (preferences[i,j] == 1 && + Math.Abs(p[i]-p[j])==1) { + Console.WriteLine("\t{0} {1}", persons[i], persons[j]); + } + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int show_all_max = 0; + if (args.Length > 0) { + show_all_max = Convert.ToInt32(args[0]); + } + + Solve(show_all_max); + } +} diff --git a/examples/dotnet/csharp/place_number_puzzle.cs b/examples/dotnet/csharp/place_number_puzzle.cs new file mode 100644 index 0000000000..624ac5f99b --- /dev/null +++ b/examples/dotnet/csharp/place_number_puzzle.cs @@ -0,0 +1,141 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class PlaceNumberPuzzle +{ + /** + * + * Place number puzzle. + * + * From + * http://ai.uwaterloo.ca/~vanbeek/Courses/Slides/introduction.pdf + * """ + * Place numbers 1 through 8 on nodes + * - each number appears exactly once + * - no connected nodes have consecutive numbers + * 2 - 5 + * / | X | \ + * 1 - 3 - 6 - 8 + * \ | X | / + * 4 - 7 + * """ + * + * Also see http://www.hakank.org/or-tools/place_number_puzzle.py + * + + */ + private static void Solve() + { + Solver solver = new Solver("PlaceNumberPuzzle"); + + // + // Data + // + int m = 32; + int n = 8; + + // Note: this is 1-based for compatibility (and lazyness) + int[,] graph = { + {1,2}, + {1,3}, + {1,4}, + {2,1}, + {2,3}, + {2,5}, + {2,6}, + {3,2}, + {3,4}, + {3,6}, + {3,7}, + {4,1}, + {4,3}, + {4,6}, + {4,7}, + {5,2}, + {5,3}, + {5,6}, + {5,8}, + {6,2}, + {6,3}, + {6,4}, + {6,5}, + {6,7}, + {6,8}, + {7,3}, + {7,4}, + {7,6}, + {7,8}, + {8,5}, + {8,6}, + {8,7} + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + for(int i = 0; i < m; i++) { + // (also base 0-base) + solver.Add( (x[graph[i,0]-1]-x[graph[i,1]-1]).Abs() > 1); + } + + // symmetry breaking + solver.Add(x[0] < x[n-1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/post_office_problem2.cs b/examples/dotnet/csharp/post_office_problem2.cs new file mode 100644 index 0000000000..2730ed60f5 --- /dev/null +++ b/examples/dotnet/csharp/post_office_problem2.cs @@ -0,0 +1,148 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class PostOfficeProblem2 +{ + + /** + * + * Post office problem. + * + * Problem statement: + * http://www-128.ibm.com/developerworks/linux/library/l-glpk2/ + * + * From Winston 'Operations Research: Applications and Algorithms': + * """ + * A post office requires a different number of full-time employees working + * on different days of the week [summarized below]. Union rules state that + * each full-time employee must work for 5 consecutive days and then receive + * two days off. For example, an employee who works on Monday to Friday + * must be off on Saturday and Sunday. The post office wants to meet its + * daily requirements using only full-time employees. Minimize the number + * of employees that must be hired. + * + * To summarize the important information about the problem: + * + * Every full-time worker works for 5 consecutive days and takes 2 days off + * - Day 1 (Monday): 17 workers needed + * - Day 2 : 13 workers needed + * - Day 3 : 15 workers needed + * - Day 4 : 19 workers needed + * - Day 5 : 14 workers needed + * - Day 6 : 16 workers needed + * - Day 7 (Sunday) : 11 workers needed + * + * The post office needs to minimize the number of employees it needs + * to hire to meet its demand. + * """ + * + * Also see http://www.hakank.org/or-tools/post_office_problem2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("PostOfficeProblem2"); + + // + // Data + // + + // days 0..6, monday 0 + int n = 7; + int[] need = {17, 13, 15, 19, 14, 16, 11}; + + // Total cost for the 5 day schedule. + // Base cost per day is 100. + // Working saturday is 100 extra + // Working sunday is 200 extra. + int[] cost = {500, 600, 800, 800, 800, 800, 700}; + + + // + // Decision variables + // + + // No. of workers starting at day i + IntVar[] x = solver.MakeIntVarArray(n, 0, 100, "x"); + + IntVar total_cost = x.ScalProd(cost).Var(); + IntVar num_workers = x.Sum().Var(); + + // + // Constraints + // + for(int i = 0; i < n; i++) { + IntVar s = (from j in Enumerable.Range(0, n) + where j != (i+5) % n && j != (i+6) % n + select x[j]).ToArray().Sum().Var(); + solver.Add(s >= need[i]); + } + + // Add a limit for the cost + solver.Add(total_cost <= 20000); + + // + + // objective + // + // + OptimizeVar obj = total_cost.Minimize(100); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_workers: {0}", num_workers.Value()); + Console.WriteLine("total_cost: {0}", total_cost.Value()); + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/quasigroup_completion.cs b/examples/dotnet/csharp/quasigroup_completion.cs new file mode 100644 index 0000000000..26846e6a93 --- /dev/null +++ b/examples/dotnet/csharp/quasigroup_completion.cs @@ -0,0 +1,229 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class QuasigroupCompletion +{ + + static int X = 0; + + /* + * default problem + * + * Example from Ruben Martins and Inès Lynce + * Breaking Local Symmetries in Quasigroup Completion Problems, page 3 + * The solution is unique: + * + * 1 3 2 5 4 + * 2 5 4 1 3 + * 4 1 3 2 5 + * 5 4 1 3 2 + * 3 2 5 4 1 + */ + static int default_n = 5; + static int[,] default_problem = {{1, X, X, X, 4}, + {X, 5, X, X, X}, + {4, X, X, 2, X}, + {X, 4, X, X, X}, + {X, X, 5, X, 1}}; + + + // for the actual problem + static int n; + static int[,] problem; + + + /** + * + * Solves the Quasigroup Completion problem. + * See http://www.hakank.org/or-tools/quasigroup_completion.py + * + */ + private static void Solve() + { + Solver solver = new Solver("QuasigroupCompletion"); + + // + // data + // + Console.WriteLine("Problem:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(problem[i,j] + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (problem[i,j] > X) { + solver.Add(x[i,j] == problem[i,j]); + } + } + } + + // + // rows and columns must be different + // + + // rows + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + } + solver.Add(row.AllDifferent()); + } + + // columns + for(int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for(int i = 0; i < n; i++) { + col[i] = x[i,j]; + } + solver.Add(col.AllDifferent()); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_SIMPLE, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Quasigroup completion file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows (n) + * < + * row number of space separated entries + * > + * + * "." or "0" means unknown, integer 1..n means known value + * + * Example + * 5 + * 1 . . . 4 + * . 5 . . . + * 4 . . 2 . + * . 4 . . . + * . . 5 . 1 + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + n = Convert.ToInt32(str); // number of rows + problem = new int[n,n]; + } else { + // the problem matrix + String[] row = Regex.Split(str, " "); + for(int i = 0; i < n; i++) { + String s = row[i]; + if (s.Equals(".")) { + problem[lineCount - 1, i] = 0; + } else { + problem[lineCount - 1, i] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + + } // end readFile + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + problem = default_problem; + n = default_n; + } + + Solve(); + } +} diff --git a/examples/dotnet/csharp/regex.cs b/examples/dotnet/csharp/regex.cs new file mode 100644 index 0000000000..d7cc0dc101 --- /dev/null +++ b/examples/dotnet/csharp/regex.cs @@ -0,0 +1,202 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class RegexGeneration +{ + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + } + + } + + + + /** + * + * Simple regular expression. + * + * My last name (Kjellerstrand) is quite often misspelled + * in ways that this regular expression shows: + * k(je|ä)ll(er|ar)?(st|b)r?an?d + * + * This model generates all the words that can be construed + * by this regular expression. + * + * + * Also see http://www.hakank.org/or-tools/regex.py + * + */ + private static void Solve(int n, List res) + { + Solver solver = new Solver("RegexGeneration"); + + Console.WriteLine("\nn: {0}", n); + + // The DFS (for regular) + int n_states = 11; + int input_max = 12; + int initial_state = 1; // 0 is for the failing state + int[] accepting_states = {12}; + + // The DFA + int [,] transition_fn = { + // 1 2 3 4 5 6 7 8 9 0 1 2 // + {0,2,3,0,0,0,0,0,0,0,0,0}, // 1 k + {0,0,0,4,0,0,0,0,0,0,0,0}, // 2 je + {0,0,0,4,0,0,0,0,0,0,0,0}, // 3 ä + {0,0,0,0,5,6,7,8,0,0,0,0}, // 4 ll + {0,0,0,0,0,0,7,8,0,0,0,0}, // 5 er + {0,0,0,0,0,0,7,8,0,0,0,0}, // 6 ar + {0,0,0,0,0,0,0,0,9,10,0,0}, // 7 st + {0,0,0,0,0,0,0,0,9,10,0,0}, // 8 b + {0,0,0,0,0,0,0,0,0,10,0,0}, // 9 r + {0,0,0,0,0,0,0,0,0,0,11,12}, // 10 a + {0,0,0,0,0,0,0,0,0,0,0,12}, // 11 n + // 12 d + }; + + // Name of the states + String[] s = {"k","je","ä","ll","er","ar","st","b","r","a","n","d"}; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, input_max, "x"); + + // + // Constraints + // + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + List res2 = new List(); + // State 1 (the start state) is not included in the + // state array (x) so we add it first. + res2.Add(s[0]); + for(int i = 0; i < n; i++) { + res2.Add(s[x[i].Value()-1]); + } + res.Add(String.Join("", res2.ToArray())); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + List res = new List(); + for(int n = 4; n <= 9; n++) { + Solve(n, res); + } + Console.WriteLine("\nThe following {0} words where generated", res.Count); + foreach(string r in res) { + Console.WriteLine(r); + } + } +} diff --git a/examples/dotnet/csharp/rogo2.cs b/examples/dotnet/csharp/rogo2.cs new file mode 100644 index 0000000000..af48fc89e9 --- /dev/null +++ b/examples/dotnet/csharp/rogo2.cs @@ -0,0 +1,356 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Rogo2 +{ + + static int W = 0; + static int B = -1; + + // Default problem + // Data from + // Mike Trick: "Operations Research, Sudoko, Rogo, and Puzzles" + // http://mat.tepper.cmu.edu/blog/?p=1302 + // + // This has 48 solutions with symmetries; + // 4 when the path symmetry is removed. + // + static int default_rows = 5; + static int default_cols = 9; + static int default_max_steps = 12; + static int default_best = 8; + static int[,] default_problem = { + {2,W,W,W,W,W,W,W,W}, + {W,3,W,W,1,W,W,2,W}, + {W,W,W,W,W,W,B,W,2}, + {W,W,2,B,W,W,W,W,W}, + {W,W,W,W,2,W,W,1,W} + }; + static String default_problem_name = "Problem Mike Trick"; + + + // The actual problem + static int rows; + static int cols; + static int max_steps; + static int best; + static int[,] problem; + static string problem_name; + + + /** + * + * Build the table of valid connections of the grid. + * + */ + public static IntTupleSet ValidConnections(int rows, int cols) + { + + IEnumerable ROWS = Enumerable.Range(0, rows); + IEnumerable COLS = Enumerable.Range(0, cols); + var result_tmp = ( + from i1 in ROWS + from j1 in COLS + from i2 in ROWS + from j2 in COLS + where + (Math.Abs(j1-j2) == 1 && i1 == i2) || + (Math.Abs(i1-i2) == 1 && j1 % cols == j2 % cols) + select new int[] {i1*cols+j1, i2*cols+j2} + ).ToArray(); + + // Convert to len x 2 matrix + int len = result_tmp.Length; + IntTupleSet result = new IntTupleSet(2); + foreach(int[] r in result_tmp) { + result.Insert(r); + } + return result; + + } + + + + /** + * + * Rogo puzzle solver. + * + * From http://www.rogopuzzle.co.nz/ + * """ + * The object is to collect the biggest score possible using a given + * number of steps in a loop around a grid. The best possible score + * for a puzzle is given with it, so you can easily check that you have + * solved the puzzle. Rogo puzzles can also include forbidden squares, + * which must be avoided in your loop. + * """ + * + * Also see Mike Trick: + * "Operations Research, Sudoko, Rogo, and Puzzles" + * http://mat.tepper.cmu.edu/blog/?p=1302 + * + * + * Also see, http://www.hakank.org/or-tools/rogo2.py + * though this model differs in a couple of central points + * which makes it much faster: + * + * - it use a table ( +AllowedAssignments) with the valid connections + * - instead of two coordinates arrays, it use a single path array + * + */ + private static void Solve() + { + + Solver solver = new Solver("Rogo2"); + + + Console.WriteLine("\n"); + Console.WriteLine("**********************************************"); + Console.WriteLine(" {0}", problem_name); + Console.WriteLine("**********************************************\n"); + + // + // Data + // + int B = -1; + + Console.WriteLine("Rows: {0} Cols: {1} Max Steps: {2}", rows, cols, max_steps); + + int[] problem_flatten = problem.Cast().ToArray(); + int max_point = problem_flatten.Max(); + int max_sum = problem_flatten.Sum(); + Console.WriteLine("max_point: {0} max_sum: {1} best: {2}", max_point, max_sum, best); + + IEnumerable STEPS = Enumerable.Range(0, max_steps); + IEnumerable STEPS1 = Enumerable.Range(0, max_steps-1); + + // the valid connections, to be used with AllowedAssignments + IntTupleSet valid_connections = ValidConnections(rows, cols); + + + // + // Decision variables + // + IntVar[] path = solver.MakeIntVarArray(max_steps, 0, rows*cols-1, "path"); + IntVar[] points = solver.MakeIntVarArray(max_steps, 0, best, "points"); + IntVar sum_points = points.Sum().VarWithName("sum_points"); + + + // + // Constraints + // + + foreach(int s in STEPS) { + // calculate the points (to maximize) + solver.Add(points[s] == problem_flatten.Element(path[s])); + + // ensure that there are no black cells in + // the path + solver.Add(problem_flatten.Element(path[s]) != B); + } + + solver.Add(path.AllDifferent()); + + + // valid connections + foreach(int s in STEPS1) { + solver.Add(new IntVar[] {path[s], path[s+1]}. + AllowedAssignments(valid_connections)); + } + // around the corner + solver.Add(new IntVar[] {path[max_steps-1], path[0]}. + AllowedAssignments(valid_connections)); + + + // Symmetry breaking + for(int s = 1; s < max_steps; s++) { + solver.Add(path[0] < path[s]); + } + + + // + // Objective + // + OptimizeVar obj = sum_points.Maximize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(path, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("sum_points: {0}", sum_points.Value()); + Console.Write("path: "); + foreach(int s in STEPS) { + Console.Write("{0} ", path[s].Value()); + } + Console.WriteLine(); + Console.WriteLine("(Adding 1 to coords...)"); + int[,] sol = new int[rows, cols]; + foreach(int s in STEPS) { + int p = (int) path[s].Value(); + int x = (int) (p / cols); + int y = (int) (p % cols); + Console.WriteLine("{0,2},{1,2} ({2} points)", x+1, y+1, points[s].Value()); + sol[x, y] = 1; + } + Console.WriteLine("\nThe path is marked by 'X's:"); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + String p = sol[i,j] == 1 ? "X" : " "; + String q = problem[i,j] == B ? "B" : + problem[i,j] == 0 ? "." : problem[i,j].ToString(); + Console.Write("{0,2}{1} ", q, p); + } + Console.WriteLine(); + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Rogo problem instance file. + * + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * rows + * cols + * max_step + * best + * + * + * Where is a rows x cols matrix of + * digits (points), W (white), B (black) + * + * """ + * # comment + * % another comment + * 5 + * 9 + * 12 + * 8 + * 2 W W W W W W W W + * W 3 W W 1 W W 2 W + * W W W W W W B W 2 + * W W 2 B W W W W W + * W W W W 2 W W 1 W + * """ + * + */ + + private static void ReadFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + String str; + int lineCount = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + Console.WriteLine(str); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + if (lineCount == 0) { + rows = Convert.ToInt32(str); + + } else if (lineCount == 1) { + cols = Convert.ToInt32(str); + problem = new int[rows, cols]; + + } else if (lineCount == 2) { + max_steps = Convert.ToInt32(str); + + } else if (lineCount == 3) { + best = Convert.ToInt32(str); + + } else { + + String[] tmp = Regex.Split(str, "[,\\s]+"); + for(int j = 0; j < cols; j++) { + int val = 0; + if (tmp[j] == "B") { + val = B; + } else if (tmp[j] == "W") { + val = W; + } else { + val = Convert.ToInt32(tmp[j]); + } + problem[lineCount-4, j] = val; + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + + + public static void Main(String[] args) + { + + rows = default_rows; + cols = default_cols; + max_steps = default_max_steps; + best = default_best; + problem = default_problem; + problem_name = default_problem_name; + + String file = ""; + + if (args.Length > 0) { + file = args[0]; + problem_name = "Problem " + file; + ReadFile(file); + } + + Solve(); + + } +} diff --git a/examples/dotnet/csharp/scheduling_speakers.cs b/examples/dotnet/csharp/scheduling_speakers.cs new file mode 100644 index 0000000000..cb9afb7bc6 --- /dev/null +++ b/examples/dotnet/csharp/scheduling_speakers.cs @@ -0,0 +1,99 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class SchedulingSpeakers +{ + + /** + * + * Scheduling speakers problem + * + * From Rina Dechter, Constraint Processing, page 72 + * Scheduling of 6 speakers in 6 slots. + * + * See http://www.hakank.org/google_or_tools/scheduling_speakers.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SchedulingSpeakers"); + + + // number of speakers + int n = 6; + + // slots available to speak + int[][] available = { + // Reasoning: + new int[] {3,4,5,6}, // 2) the only one with 6 after speaker F -> 1 + new int[] {3,4}, // 5) 3 or 4 + new int[] {2,3,4,5}, // 3) only with 5 after F -> 1 and A -> 6 + new int[] {2,3,4}, // 4) only with 2 after C -> 5 and F -> 1 + new int[] {3,4}, // 5) 3 or 4 + new int[] {1,2,3,4,5,6} // 1) the only with 1 + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + for(int i = 0; i < n; i++) { + solver.Add(x[i].Member(available[i])); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine(string.Join(",", (from i in x select i.Value()))); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/secret_santa.cs b/examples/dotnet/csharp/secret_santa.cs new file mode 100644 index 0000000000..417789d186 --- /dev/null +++ b/examples/dotnet/csharp/secret_santa.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class SecretSanta +{ + + /** + * + * Secret Santa problem in Google CP Solver. + * + * From Ruby Quiz Secret Santa + * http://www.rubyquiz.com/quiz2.html + * """ + * Honoring a long standing tradition started by my wife's dad, my friends + * all play a Secret Santa game around Christmas time. We draw names and + * spend a week sneaking that person gifts and clues to our identity. On the + * last night of the game, we get together, have dinner, share stories, and, + * most importantly, try to guess who our Secret Santa was. It's a crazily + * fun way to enjoy each other's company during the holidays. + * + * To choose Santas, we use to draw names out of a hat. This system was + * tedious, prone to many 'Wait, I got myself...' problems. This year, we + * made a change to the rules that further complicated picking and we knew + * the hat draw would not stand up to the challenge. Naturally, to solve + * this problem, I scripted the process. Since that turned out to be more + * interesting than I had expected, I decided to share. + * + * This weeks Ruby Quiz is to implement a Secret Santa selection script. + * * Your script will be fed a list of names on STDIN. + * ... + * Your script should then choose a Secret Santa for every name in the list. + * Obviously, a person cannot be their own Secret Santa. In addition, my friends + * no longer allow people in the same family to be Santas for each other and your + * script should take this into account. + * """ + * + * Comment: This model skips the file input and mail parts. We + * assume that the friends are identified with a number from 1..n, + * and the families is identified with a number 1..num_families. + * + * Also see http://www.hakank.org/or-tools/secret_santa.py + * Also see http://www.hakank.org/or-tools/secret_santa2.cs + * + */ + private static void Solve() + { + + Solver solver = new Solver("SecretSanta"); + + int[] family = {1,1,1,1, 2, 3,3,3,3,3, 4,4}; + int n = family.Length; + + Console.WriteLine("n = {0}", n); + + IEnumerable RANGE = Enumerable.Range(0, n); + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + // Can't be one own"s Secret Santa + // (i.e. ensure that there are no fix-point in the array.) + foreach(int i in RANGE) { + solver.Add(x[i] != i); + } + + + // No Secret Santa to a person in the same family + foreach(int i in RANGE) { + solver.Add(solver.MakeIntConst(family[i]) != family.Element(x[i])); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_SIMPLE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/secret_santa2.cs b/examples/dotnet/csharp/secret_santa2.cs new file mode 100644 index 0000000000..9467c995b5 --- /dev/null +++ b/examples/dotnet/csharp/secret_santa2.cs @@ -0,0 +1,248 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class SecretSanta2 +{ + + /** + * + * Secret Santa problem II in Google CP Solver. + * + * From Maple Primes: 'Secret Santa Graph Theory' + * http://www.mapleprimes.com/blog/jpmay/secretsantagraphtheory + * """ + * Every year my extended family does a 'secret santa' gift exchange. + * Each person draws another person at random and then gets a gift for + * them. At first, none of my siblings were married, and so the draw was + * completely random. Then, as people got married, we added the restriction + * that spouses should not draw each others names. This restriction meant + * that we moved from using slips of paper on a hat to using a simple + * computer program to choose names. Then people began to complain when + * they would get the same person two years in a row, so the program was + * modified to keep some history and avoid giving anyone a name in their + * recent history. This year, not everyone was participating, and so after + * removing names, and limiting the number of exclusions to four per person, + * I had data something like this: + * + * Name: Spouse, Recent Picks + * + * Noah: Ava. Ella, Evan, Ryan, John + * Ava: Noah, Evan, Mia, John, Ryan + * Ryan: Mia, Ella, Ava, Lily, Evan + * Mia: Ryan, Ava, Ella, Lily, Evan + * Ella: John, Lily, Evan, Mia, Ava + * John: Ella, Noah, Lily, Ryan, Ava + * Lily: Evan, John, Mia, Ava, Ella + * Evan: Lily, Mia, John, Ryan, Noah + * """ + * + * Note: I interpret this as the following three constraints: + * 1) One cannot be a Secret Santa of one's spouse + * 2) One cannot be a Secret Santa for somebody two years in a row + * 3) Optimization: maximize the time since the last time + * + * This model also handle single persons, something the original + * problem don't mention. + * + * + * Also see http://www.hakank.org/or-tools/secret_santa2.py + * + */ + private static void Solve(int single=0) + { + + Solver solver = new Solver("SecretSanta2"); + + Console.WriteLine("\nSingle: {0}", single); + + // + // The matrix version of earlier rounds. + // M means that no earlier Santa has been assigned. + // Note: Ryan and Mia has the same recipient for years 3 and 4, + // and Ella and John has for year 4. + // This seems to be caused by modification of + // original data. + // + int n_no_single = 8; + int M = n_no_single + 1; + int[][] rounds_no_single = { + // N A R M El J L Ev + new int[] {0, M, 3, M, 1, 4, M, 2}, // Noah + new int[] {M, 0, 4, 2, M, 3, M, 1}, // Ava + new int[] {M, 2, 0, M, 1, M, 3, 4}, // Ryan + new int[] {M, 1, M, 0, 2, M, 3, 4}, // Mia + new int[] {M, 4, M, 3, 0, M, 1, 2}, // Ella + new int[] {1, 4, 3, M, M, 0, 2, M}, // John + new int[] {M, 3, M, 2, 4, 1, 0, M}, // Lily + new int[] {4, M, 3, 1, M, 2, M, 0} // Evan + }; + + // + // Rounds with a single person (fake data) + // + int n_with_single = 9; + M = n_with_single + 1; + int[][] rounds_single = { + // N A R M El J L Ev S + new int[] {0, M, 3, M, 1, 4, M, 2, 2}, // Noah + new int[] {M, 0, 4, 2, M, 3, M, 1, 1}, // Ava + new int[] {M, 2, 0, M, 1, M, 3, 4, 4}, // Ryan + new int[] {M, 1, M, 0, 2, M, 3, 4, 3}, // Mia + new int[] {M, 4, M, 3, 0, M, 1, 2, M}, // Ella + new int[] {1, 4, 3, M, M, 0, 2, M, M}, // John + new int[] {M, 3, M, 2, 4, 1, 0, M, M}, // Lily + new int[] {4, M, 3, 1, M, 2, M, 0, M}, // Evan + new int[] {1, 2, 3, 4, M, 2, M, M, 0} // Single + }; + + + int Noah = 0; + int Ava = 1; + int Ryan = 2; + int Mia = 3; + int Ella = 4; + int John = 5; + int Lily = 6; + int Evan = 7; + + int n = n_no_single; + + int[][] rounds = rounds_no_single; + if (single == 1) { + n = n_with_single; + rounds = rounds_single; + } + M = n + 1; + + IEnumerable RANGE = Enumerable.Range(0, n); + + + + String[] persons = {"Noah", "Ava", "Ryan", "Mia", "Ella", + "John", "Lily", "Evan", "Single"}; + + int[] spouses = { + Ava, // Noah + Noah, // Ava + Mia, // Rya + Ryan, // Mia + John, // Ella + Ella, // John + Evan, // Lily + Lily, // Evan + -1 // Single has no spouse + }; + + + // + // Decision variables + // + IntVar[] santas = solver.MakeIntVarArray(n, 0, n-1, "santas"); + IntVar[] santa_distance = solver.MakeIntVarArray(n, 0, M, "santa_distance"); + + // total of "distance", to maximize + IntVar z = santa_distance.Sum().VarWithName("z"); + + + // + // Constraints + // + solver.Add(santas.AllDifferent()); + + // Can't be one own"s Secret Santa + // (i.e. ensure that there are no fix-point in the array.) + foreach(int i in RANGE) { + solver.Add(santas[i] != i); + } + + + // no Santa for a spouses + foreach(int i in RANGE) { + if (spouses[i] > -1) { + solver.Add(santas[i] != spouses[i]); + } + } + + // optimize "distance" to earlier rounds: + foreach(int i in RANGE) { + solver.Add(santa_distance[i] == rounds[i].Element(santas[i])); + } + + + // cannot be a Secret Santa for the same person + // two years in a row. + foreach(int i in RANGE) { + foreach(int j in RANGE) { + if (rounds[i][j] == 1) { + solver.Add(santas[i] != j); + } + } + } + + + // + // Objective (minimize the distances) + // + OptimizeVar obj = z.Maximize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(santas, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("\ntotal distances: {0}", z.Value()); + Console.Write("santas: "); + for(int i = 0; i < n; i++) { + Console.Write(santas[i].Value() + " "); + } + Console.WriteLine(); + foreach(int i in RANGE) { + Console.WriteLine("{0}\tis a Santa to {1} (distance {2})", + persons[i], + persons[santas[i].Value()], + santa_distance[i].Value()); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int single = 0; + Solve(single); + single = 1; + Solve(single); + } +} diff --git a/examples/dotnet/csharp/send_more_money.cs b/examples/dotnet/csharp/send_more_money.cs new file mode 100644 index 0000000000..e047ddb703 --- /dev/null +++ b/examples/dotnet/csharp/send_more_money.cs @@ -0,0 +1,82 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class SendMoreMoney +{ + /** + * + * Solve the SEND+MORE=MONEY problem + * + */ + private static void Solve() + { + Solver solver = new Solver("SendMoreMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar R = solver.MakeIntVar(0, 9, "R"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,R,Y}; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(S*1000 + E*100 + N*10 + D + M*1000 + O*100 + R*10 + E == + M*10000 + O*1000 + N*100 + E*10 + Y); + + solver.Add(S > 0); + solver.Add(M > 0); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < 8; i++) { + Console.Write(x[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/send_more_money2.cs b/examples/dotnet/csharp/send_more_money2.cs new file mode 100644 index 0000000000..2141d5ce94 --- /dev/null +++ b/examples/dotnet/csharp/send_more_money2.cs @@ -0,0 +1,94 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class SendMoreMoney +{ + /** + * + * Solve the SEND+MORE=MONEY problem + * using scalar product. + * + */ + private static void Solve() + { + Solver solver = new Solver("SendMoreMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar R = solver.MakeIntVar(0, 9, "R"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,R,Y}; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + /* + solver.Add(S*1000 + E*100 + N*10 + D + M*1000 + O*100 + R*10 + E == + M*10000 + O*1000 + N*100 + E*10 + Y); + */ + + // Here we use scalar product instead. + int[] s1 = new int[] {1000,100,10,1}; + int[] s2 = new int[] {10000,1000,100,10,1}; + solver.Add(new IntVar[] {S,E,N,D}.ScalProd(s1) + + new IntVar[] {M,O,R,E}.ScalProd(s1) == + new IntVar[] {M,O,N,E,Y}.ScalProd(s2)); + + solver.Add(S > 0); + solver.Add(M > 0); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < 8; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0}", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/send_most_money.cs b/examples/dotnet/csharp/send_most_money.cs new file mode 100644 index 0000000000..1fdf31edc2 --- /dev/null +++ b/examples/dotnet/csharp/send_most_money.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class SendMostMoney +{ + /** + * + * Solve the SEND+MOST=MONEY problem + * where the object is to maximize MONEY + * See http://www.hakank.org/google_or_tools/send_most_money.py + * + */ + private static long Solve(long MONEY) + { + + Solver solver = new Solver("SendMostMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar T = solver.MakeIntVar(0, 9, "T"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,T,Y}; + + IntVar[] eq = {S,E,N,D, M,O,S,T, M,O,N,E,Y}; + int[] coeffs = { 1000, 100, 10, 1, // S E N D + + 1000, 100, 10, 1, // M O S T + -10000,-1000, -100,-10,-1 // == M O N E Y + }; + solver.Add(eq.ScalProd(coeffs) == 0); + + // IntVar money = solver.MakeScalProd(new IntVar[] {M, O, N, E, Y}, + // new int[] {10000, 1000, 100, 10, 1}).Var(); + IntVar money = (new IntVar[] {M, O, N, E, Y}). + ScalProd(new int[] {10000, 1000, 100, 10, 1}).Var(); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(S > 0); + solver.Add(M > 0); + + if (MONEY > 0) { + solver.Add(money == MONEY); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (MONEY == 0) { + OptimizeVar obj = money.Maximize(1); + solver.NewSearch(db, obj); + } else { + solver.NewSearch(db); + } + + long money_ret = 0; + while (solver.NextSolution()) { + money_ret = money.Value(); + Console.WriteLine("money: {0}", money.Value() ); + for(int i = 0; i < x.Length; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + return money_ret; + + } + + public static void Main(String[] args) + { + Console.WriteLine("First get the max value of money:"); + long this_money = Solve(0); + Console.WriteLine("\nThen we find all solutions for MONEY = {0}:", this_money); + long tmp = Solve(this_money); + } +} diff --git a/examples/dotnet/csharp/seseman.cs b/examples/dotnet/csharp/seseman.cs new file mode 100644 index 0000000000..51e0413434 --- /dev/null +++ b/examples/dotnet/csharp/seseman.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + + +public class Seseman +{ + + /** + * + * Solves the Seseman convent problem. + * See http://www.hakank.org/google_or_tools/seseman.py + * + */ + private static void Solve(int n = 3) + { + Solver solver = new Solver("Seseman"); + + // + // data + // + int border_sum = n * n; + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, n*n, "x"); + IntVar[] x_flat = x.Flatten(); + IntVar total_sum = x_flat.Sum().Var(); + + + // + // Constraints + // + + // zero in all middle cells + for(int i = 1; i < n-1; i++) { + for(int j = 1; j < n-1; j++) { + solver.Add(x[i,j] == 0); + } + } + + // all borders must be >= 1 + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i == 0 || j == 0 || i == n - 1 || j == n - 1) { + solver.Add(x[i,j] >= 1); + } + } + } + + // sum the four borders + IntVar[] border1 = new IntVar[n]; + IntVar[] border2 = new IntVar[n]; + IntVar[] border3 = new IntVar[n]; + IntVar[] border4 = new IntVar[n]; + for(int i = 0; i < n; i++) { + border1[i] = x[i,0]; + border2[i] = x[i,n-1]; + border3[i] = x[0,i]; + border4[i] = x[n-1,i]; + } + solver.Add(border1.Sum() == border_sum); + solver.Add(border2.Sum() == border_sum); + solver.Add(border3.Sum() == border_sum); + solver.Add(border4.Sum() == border_sum); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.WriteLine("total_sum: {0} ", total_sum.Value()); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 3; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/examples/dotnet/csharp/set_covering.cs b/examples/dotnet/csharp/set_covering.cs new file mode 100644 index 0000000000..3c2de49f9d --- /dev/null +++ b/examples/dotnet/csharp/set_covering.cs @@ -0,0 +1,109 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering"); + + // + // data + // + + // Placing of firestations, from Winston 'Operations Research', + // page 486. + int min_distance = 15; + int num_cities = 6; + + int[,] distance = {{ 0,10,20,30,30,20}, + {10, 0,25,35,20,10}, + {20,25, 0,15,30,20}, + {30,35,15, 0,15,25}, + {30,20,30,15, 0,14}, + {20,10,20,25,14, 0}}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_cities, 0, 1, "x"); + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that all cities are covered + for(int i = 0; i < num_cities; i++) { + IntVar[] b = (from j in Enumerable.Range(0, num_cities) + where distance[i,j] <= min_distance + select x[j]).ToArray(); + solver.Add(b.Sum() >= 1); + + } + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.Write("x: "); + for(int i = 0; i < num_cities; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/set_covering2.cs b/examples/dotnet/csharp/set_covering2.cs new file mode 100644 index 0000000000..8899c046ac --- /dev/null +++ b/examples/dotnet/csharp/set_covering2.cs @@ -0,0 +1,115 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering2 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering2"); + + // + // data + // + + // Example 9.1-2 from + // Taha "Operations Research - An Introduction", + // page 354ff. + // Minimize the number of security telephones in street + // corners on a campus. + + int n = 8; // maximum number of corners + int num_streets = 11; // number of connected streets + + // corners of each street + // Note: 1-based (handled below) + int[,] corner = {{1,2}, + {2,3}, + {4,5}, + {7,8}, + {6,7}, + {2,6}, + {1,6}, + {4,7}, + {2,4}, + {5,8}, + {3,5}}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + // number of telephones, to be minimized + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that all streets are covered + for(int i = 0; i < num_streets; i++) { + solver.Add(x[corner[i,0] - 1] + x[corner[i,1] - 1] >= 1); + } + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/set_covering3.cs b/examples/dotnet/csharp/set_covering3.cs new file mode 100644 index 0000000000..012a496c8d --- /dev/null +++ b/examples/dotnet/csharp/set_covering3.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering3 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering3.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering3"); + + // + // data + // + + // Set covering problem from + // Katta G. Murty: 'Optimization Models for Decision Making', + // page 302f + // http://ioe.engin.umich.edu/people/fac/books/murty/opti_model/junior-7.pdf + int num_groups = 6; + int num_senators = 10; + + // which group does a senator belong to? + int[,] belongs = {{1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, // 1 southern + {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, // 2 northern + {0, 1, 1, 0, 0, 0, 0, 1, 1, 1}, // 3 liberals + {1, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 4 conservative + {0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, // 5 democrats + {1, 1, 0, 0, 0, 0, 0, 1, 0, 1}}; // 6 republicans + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_senators, 0, 1, "x"); + // number of assigned senators, to be minimized + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that each group is covered by at least + // one senator + for(int i = 0; i < num_groups; i++) { + IntVar[] b = new IntVar[num_senators]; + for(int j = 0; j < num_senators; j++) { + b[j] = (x[j]*belongs[i,j]).Var(); + } + solver.Add(b.Sum() >= 1); + } + + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: " + z.Value()); + Console.Write("x: "); + for(int j = 0; j < num_senators; j++) { + Console.Write(x[j].Value() + " "); + } + Console.WriteLine(); + + // More details + for(int j = 0; j < num_senators; j++) { + if (x[j].Value() == 1) { + Console.Write("Senator " + (1 + j) + + " belongs to these groups: "); + for(int i = 0; i < num_groups; i++) { + if (belongs[i,j] == 1) { + Console.Write((1 + i) + " "); + } + } + Console.WriteLine(); + } + + } + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/set_covering4.cs b/examples/dotnet/csharp/set_covering4.cs new file mode 100644 index 0000000000..1fcc4435f9 --- /dev/null +++ b/examples/dotnet/csharp/set_covering4.cs @@ -0,0 +1,134 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering4 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering4.py + * + */ + private static void Solve(int set_partition) + { + + Solver solver = new Solver("SetCovering4"); + + // + // data + // + + // Set partition and set covering problem from + // Example from the Swedish book + // Lundgren, Roennqvist, Vaebrand + // 'Optimeringslaera' (translation: 'Optimization theory'), + // page 408. + int num_alternatives = 10; + int num_objects = 8; + + // costs for the alternatives + int[] costs = {19, 16, 18, 13, 15, 19, 15, 17, 16, 15}; + + // the alternatives, and their objects + int[,] a = { + // 1 2 3 4 5 6 7 8 the objects + {1,0,0,0,0,1,0,0}, // alternative 1 + {0,1,0,0,0,1,0,1}, // alternative 2 + {1,0,0,1,0,0,1,0}, // alternative 3 + {0,1,1,0,1,0,0,0}, // alternative 4 + {0,1,0,0,1,0,0,0}, // alternative 5 + {0,1,1,0,0,0,0,0}, // alternative 6 + {0,1,1,1,0,0,0,0}, // alternative 7 + {0,0,0,1,1,0,0,1}, // alternative 8 + {0,0,1,0,0,1,0,1}, // alternative 9 + {1,0,0,0,0,1,1,0}}; // alternative 10 + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_alternatives, 0, 1, "x"); + // number of assigned senators, to be minimized + IntVar z = x.ScalProd(costs).VarWithName("z"); + + // + // Constraints + // + + + for(int j = 0; j < num_objects; j++) { + IntVar[] b = new IntVar[num_alternatives]; + for(int i = 0; i < num_alternatives; i++) { + b[i] = (x[i] * a[i,j]).Var(); + } + + if (set_partition == 1) { + solver.Add(b.Sum() >= 1); + } else { + solver.Add(b.Sum() == 1); + } + } + + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: " + z.Value()); + Console.Write("Selected alternatives: "); + for(int i = 0; i < num_alternatives; i++) { + if (x[i].Value() == 1) { + Console.Write((i+1) + " "); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Console.WriteLine("Set partition:"); + Solve(1); + Console.WriteLine("\nSet covering:"); + Solve(0); + } +} diff --git a/examples/dotnet/csharp/set_covering_deployment.cs b/examples/dotnet/csharp/set_covering_deployment.cs new file mode 100644 index 0000000000..3135fb402a --- /dev/null +++ b/examples/dotnet/csharp/set_covering_deployment.cs @@ -0,0 +1,149 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCoveringDeployment +{ + + /** + * + * Solves a set covering deployment problem. + * See See http://www.hakank.org/or-tools/set_covering_deployment.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringDeployment"); + + // + // data + // + + // From http://mathworld.wolfram.com/SetCoveringDeployment.html + string[] countries = {"Alexandria", + "Asia Minor", + "Britain", + "Byzantium", + "Gaul", + "Iberia", + "Rome", + "Tunis"}; + + int n = countries.Length; + + // the incidence matrix (neighbours) + int[,] mat = {{0, 1, 0, 1, 0, 0, 1, 1}, + {1, 0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, 1, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 1, 0}, + {0, 0, 1, 0, 1, 0, 1, 1}, + {1, 0, 0, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 1, 0}}; + + // + // Decision variables + // + + // First army + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + + // Second (reserve) army + IntVar[] y = solver.MakeIntVarArray(n, 0, 1, "y"); + + // total number of armies + IntVar num_armies = (x.Sum() + y.Sum()).Var(); + + + // + // Constraints + // + + // + // Constraint 1: There is always an army in a city + // (+ maybe a backup) + // Or rather: Is there a backup, there + // must be an an army + // + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= y[i]); + } + + // + // Constraint 2: There should always be an backup + // army near every city + // + for(int i = 0; i < n; i++) { + IntVar[] count_neighbours = ( + from j in Enumerable.Range(0, n) + where mat[i,j] == 1 + select(y[j])).ToArray(); + + solver.Add((x[i] + count_neighbours.Sum()) >= 1); + + } + + + // + // objective + // + OptimizeVar objective = num_armies.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("num_armies: " + num_armies.Value()); + for(int i = 0; i < n; i++) { + if (x[i].Value() == 1) { + Console.Write("Army: " + countries[i] + " "); + } + + if (y[i].Value() == 1) { + Console.WriteLine(" Reverse army: " + countries[i]); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/set_covering_skiena.cs b/examples/dotnet/csharp/set_covering_skiena.cs new file mode 100644 index 0000000000..5446e00349 --- /dev/null +++ b/examples/dotnet/csharp/set_covering_skiena.cs @@ -0,0 +1,126 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class SetCoveringSkiena +{ + /** + * + * Set covering. + * + * Example from Steven Skiena, The Stony Brook Algorithm Repository + * http://www.cs.sunysb.edu/~algorith/files/set-cover.shtml + * """ + * Input Description: A set of subsets S_1, ..., S_m of the + * universal set U = {1,...,n}. + * + * Problem: What is the smallest subset of subsets T subset S such + * that \cup_{t_i in T} t_i = U? + * """ + * Data is from the pictures INPUT/OUTPUT. + * + * + * Also see http://www.hakank.org/or-tools/set_covering_skiena.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringSkiena"); + + int num_sets = 7; + int num_elements = 12; + IEnumerable Sets = Enumerable.Range(0, num_sets); + IEnumerable Elements = Enumerable.Range(0, num_elements); + + // Which element belongs to which set + int[,] belongs = + { + // 1 2 3 4 5 6 7 8 9 0 1 2 elements + {1,1,0,0,0,0,0,0,0,0,0,0}, // Set 1 + {0,1,0,0,0,0,0,1,0,0,0,0}, // 2 + {0,0,0,0,1,1,0,0,0,0,0,0}, // 3 + {0,0,0,0,0,1,1,0,0,1,1,0}, // 4 + {0,0,0,0,0,0,0,0,1,1,0,0}, // 5 + {1,1,1,0,1,0,0,0,1,1,1,0}, // 6 + {0,0,1,1,0,0,1,1,0,0,1,1} // 7 + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_sets, 0, 1, "x"); + IntVar z = x.Sum().VarWithName("z"); + // total number of elements in the choosen sets + IntVar tot_elements = solver.MakeIntVar(0, num_sets*num_elements, "tot_elements"); + + + // + // Constraints + // + + // all sets must be used + foreach(int j in Elements) { + solver.Add( (from i in Sets select belongs[i,j] * x[i]) + .ToArray().Sum() >= 1); + } + + // number of used elements + solver.Add((from i in Sets from j in Elements select x[i] * belongs[i,j]) + .ToArray().Sum() == tot_elements); + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.WriteLine("tot_elements: {0}", tot_elements.Value()); + Console.WriteLine( + "x: {0}", + String.Join(" ", (from i in Enumerable.Range(0, num_sets) + select x[i].Value().ToString()).ToArray())); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/set_partition.cs b/examples/dotnet/csharp/set_partition.cs new file mode 100644 index 0000000000..e7900f3f1c --- /dev/null +++ b/examples/dotnet/csharp/set_partition.cs @@ -0,0 +1,204 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class SetPartition +{ + + + // + // Partition the sets (binary matrix representation). + // + public static void partition_sets(Solver solver, + IntVar[,] x, int num_sets, int n) + { + + for(int i = 0; i Sets = Enumerable.Range(0, num_sets); + IEnumerable NRange = Enumerable.Range(0, n); + + + // + // Decision variables + // + IntVar[,] a = solver.MakeIntVarMatrix(num_sets, n, 0, 1, "a"); + IntVar[] a_flat = a.Flatten(); + + + // + // Constraints + // + + // partition set + partition_sets(solver, a, num_sets, n); + + foreach(int i in Sets) { + foreach(int j in Sets) { + + // same cardinality + solver.Add( + (from k in NRange select a[i,k]).ToArray().Sum() + == + (from k in NRange select a[j,k]).ToArray().Sum()); + + // same sum + solver.Add( + (from k in NRange select (k*a[i,k])).ToArray().Sum() + == + (from k in NRange select (k*a[j,k])).ToArray().Sum()); + + + // same sum squared + solver.Add( + (from k in NRange select (k*a[i,k]*k*a[i,k])).ToArray().Sum() + == + (from k in NRange select (k*a[j,k]*k*a[j,k])).ToArray().Sum()); + } + } + + + // symmetry breaking for num_sets == 2 + if (num_sets == 2) { + solver.Add(a[0,0] == 1); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(a_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + + int[,] a_val = new int[num_sets, n]; + foreach(int i in Sets) { + foreach(int j in NRange) { + a_val[i,j] = (int)a[i,j].Value(); + } + } + Console.WriteLine("sums: {0}", + (from j in NRange + select (j+1)*a_val[0,j]).ToArray().Sum()); + + Console.WriteLine("sums squared: {0}", + (from j in NRange + select (int)Math.Pow((j+1)*a_val[0,j],2)).ToArray().Sum()); + + // Show the numbers in each set + foreach(int i in Sets) { + if ( (from j in NRange select a_val[i,j]).ToArray().Sum() > 0 ) { + Console.Write(i+1 + ": "); + foreach(int j in NRange) { + if (a_val[i,j] == 1) { + Console.Write((j+1) + " "); + } + } + Console.WriteLine(); + } + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 16; + int num_sets = 2; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num_sets = Convert.ToInt32(args[1]); + } + + if (n % num_sets == 0) { + + Solve(n, num_sets); + } else { + Console.WriteLine("n {0} num_sets {1}: Equal sets is not possible!", + n, num_sets); + } + } +} diff --git a/examples/dotnet/csharp/sicherman_dice.cs b/examples/dotnet/csharp/sicherman_dice.cs new file mode 100644 index 0000000000..d0cc8e573e --- /dev/null +++ b/examples/dotnet/csharp/sicherman_dice.cs @@ -0,0 +1,145 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class SichermanDice +{ + /** + * + * Sicherman Dice. + * + * From http://en.wikipedia.org/wiki/Sicherman_dice + * "" + * Sicherman dice are the only pair of 6-sided dice which are not normal dice, + * bear only positive integers, and have the same probability distribution for + * the sum as normal dice. + * + * The faces on the dice are numbered 1, 2, 2, 3, 3, 4 and 1, 3, 4, 5, 6, 8. + * "" + * + * I read about this problem in a book/column by Martin Gardner long + * time ago, and got inspired to model it now by the WolframBlog post + * "Sicherman Dice": http://blog.wolfram.com/2010/07/13/sicherman-dice/ + * + * This model gets the two different ways, first the standard way and + * then the Sicherman dice: + * + * x1 = [1, 2, 3, 4, 5, 6] + * x2 = [1, 2, 3, 4, 5, 6] + * ---------- + * x1 = [1, 2, 2, 3, 3, 4] + * x2 = [1, 3, 4, 5, 6, 8] + * + * + * Extra: If we also allow 0 (zero) as a valid value then the + * following two solutions are also valid: + * + * x1 = [0, 1, 1, 2, 2, 3] + * x2 = [2, 4, 5, 6, 7, 9] + * ---------- + * x1 = [0, 1, 2, 3, 4, 5] + * x2 = [2, 3, 4, 5, 6, 7] + * + * These two extra cases are mentioned here: + * http://mathworld.wolfram.com/SichermanDice.html + * + * + * Also see http://www.hakank.org/or-tools/sicherman_dice.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SichermanDice"); + + // + // Data + // + int n = 6; + int m = 10; + int lowest_value = 0; + + // standard distribution + int[] standard_dist = {1,2,3,4,5,6,5,4,3,2,1}; + + + IEnumerable RANGE = Enumerable.Range(0, n); + IEnumerable RANGE1 = Enumerable.Range(0, n-1); + + + // + // Decision variables + // + + IntVar[] x1 = solver.MakeIntVarArray(n, lowest_value, m, "x1"); + IntVar[] x2 = solver.MakeIntVarArray(n, lowest_value, m, "x2"); + + // + // Constraints + // + for(int k = 0; k < standard_dist.Length; k++) { + solver.Add((from i in RANGE + from j in RANGE + select x1[i] + x2[j] == k + 2 + ).ToArray().Sum() == standard_dist[k]); + } + + // symmetry breaking + foreach(int i in RANGE1) { + solver.Add(x1[i] <= x1[i+1]); + solver.Add(x2[i] <= x2[i+1]); + solver.Add(x1[i] <= x2[i]); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x1.Concat(x2).ToArray(), + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x1: "); + foreach(int i in RANGE) { + Console.Write(x1[i].Value() + " "); + } + Console.Write("\nx2: "); + foreach(int i in RANGE) { + Console.Write(x2[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/ski_assignment.cs b/examples/dotnet/csharp/ski_assignment.cs new file mode 100644 index 0000000000..e3b520e5b6 --- /dev/null +++ b/examples/dotnet/csharp/ski_assignment.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class SkiAssignment +{ + /** + * + * Ski assignment in Google CP Solver. + * + * From Jeffrey Lee Hellrung, Jr.: + * PIC 60, Fall 2008 Final Review, December 12, 2008 + * http://www.math.ucla.edu/~jhellrun/course_files/Fall%25202008/PIC%252060%2520-%2520Data%2520Structures%2520and%2520Algorithms/final_review.pdf + * """ + * 5. Ski Optimization! Your job at Snapple is pleasant but in the winter + * you've decided to become a ski bum. You've hooked up with the Mount + * Baldy Ski Resort. They'll let you ski all winter for free in exchange + * for helping their ski rental shop with an algorithm to assign skis to + * skiers. Ideally, each skier should obtain a pair of skis whose height + * matches his or her own height exactly. Unfortunately, this is generally + * not possible. We define the disparity between a skier and his or her + * skis to be the absolute value of the difference between the height of + * the skier and the pair of skis. Our objective is to find an assignment + * of skis to skiers that minimizes the sum of the disparities. + * ... + * Illustrate your algorithm by explicitly filling out the A[i, j] table + * for the following sample data: + * - Ski heights : 1, 2, 5, 7, 13, 21. + * - Skier heights: 3, 4, 7, 11, 18. + * """ + * + * Also see http://www.hakank.org/or-tools/ski_assignment.py + * + + */ + private static void Solve() + { + Solver solver = new Solver("SkiAssignment"); + + // + // Data + // + int num_skis = 6; + int num_skiers = 5; + int[] ski_heights = {1, 2, 5, 7, 13, 21}; + int[] skier_heights = {3, 4, 7, 11, 18}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_skiers, 0, num_skis-1, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + IntVar[] z_tmp = new IntVar[num_skiers]; + for(int i = 0; i < num_skiers; i++) { + z_tmp[i] = (ski_heights.Element(x[i]) - skier_heights[i]).Abs().Var(); + } + + // IntVar z = solver.MakeIntVar(0, ski_heights.Sum(), "z"); + // solver.Add(z_tmp.Sum() == z); + // The direct cast from IntExpr to IntVar is potentially faster than + // the above code. + IntVar z = z_tmp.Sum().VarWithName("z"); + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("z: {0} x: ", z.Value()); + for(int i = 0; i < num_skiers; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/slow_scheduling.cs b/examples/dotnet/csharp/slow_scheduling.cs new file mode 100644 index 0000000000..2127431c46 --- /dev/null +++ b/examples/dotnet/csharp/slow_scheduling.cs @@ -0,0 +1,284 @@ +// +// Copyright 2013 Google +// +// 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. + +using Google.OrTools.ConstraintSolver; +using Google.OrTools.Graph; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System; + +public class SpeakerScheduling +{ + + public class FlowAssign : NetDecisionBuilder + { + public FlowAssign(IntVar[] vars, int first_slot, IntVar last_slot_var) + { + vars_ = vars; + first_slot_ = first_slot; + last_slot_var_ = last_slot_var; + } + + public override Decision Next(Solver solver) + { + int large = 100000; + int number_of_variables = vars_.Length; + long last_slot = last_slot_var_.Max(); + // Lets build a bipartite graph with equal number of nodes left and right. + // Variables will be on the left, slots on the right. + // We will add dummy variables when needed. + // Arcs will have a cost x is slot x is possible for a variable, a large + // number otherwise. For dummy variables, the cost will be 0 always. + LinearSumAssignment matching = new LinearSumAssignment(); + for (int speaker = 0; speaker < number_of_variables; ++speaker) + { + IntVar var = vars_[speaker]; + for (int value = first_slot_; value <= last_slot; ++value) + { + if (var.Contains(value)) + { + matching.AddArcWithCost(speaker, value - first_slot_, value); + } + else + { + matching.AddArcWithCost(speaker, value - first_slot_, large); + } + } + } + // The Matching algorithms expect the same number of left and right nodes. + // So we fill the rest with dense zero-cost arcs. + for (int dummy = number_of_variables; + dummy <= last_slot - first_slot_; ++dummy) { + for (int value = first_slot_; value <= last_slot; ++value) + { + matching.AddArcWithCost(dummy, value - first_slot_, 0); + } + } + if (matching.Solve() == LinearSumAssignment.OPTIMAL && + matching.OptimalCost() < large) // No violated arcs. + { + for (int speaker = 0; speaker < number_of_variables; ++speaker) + { + vars_[speaker].SetValue(matching.RightMate(speaker) + first_slot_); + } + } else { + solver.Fail(); + } + return null; + } + + private IntVar[] vars_; + private int first_slot_; + private IntVar last_slot_var_; + } + + private static void Solve(int first_slot) + { + Console.WriteLine("----------------------------------------------------"); + Solver solver = new Solver("SpeakerScheduling"); + + // the slots each speaker is available + int[][] speaker_availability = { + new int[] {1,3,4,6,7,10,12,13,14,15,16,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59}, + new int[] {1,2,7,8,10,11,16,17,18,21,22,23,24,25,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,52,53,54,55,56,57,58,59,60}, + new int[] {5,6,7,10,12,14,16,17,21,22,23,24,33,35,37,38,39,40,41,42,43,44,45,46,51,53,55,56,57,58,59}, + new int[] {1,2,3,4,5,6,7,11,13,14,15,16,20,24,25,33,34,35,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,60}, + new int[] {4,7,8,9,16,17,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,49,50,51,53,55,56,57,58,59,60}, + new int[] {4,7,9,11,12,13,14,15,16,17,18,22,23,24,33,34,35,36,38,39,42,44,46,48,49,51,53,54,55,56,57}, + new int[] {1,2,3,4,5,6,7,33,34,35,36,37,38,39,40,41,42,54,55,56,57,58,59,60}, + new int[] {1,3,11,14,15,16,17,21,22,23,24,25,33,35,36,37,39,40,41,42,43,44,45,47,48,49,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,7,8,9,10,11,13,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,50,51,52,53,54,55,56,57,59,60}, + new int[] {24,33,34,35,36,37,38,39,40,41,42,43,45,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,51,52,53,55,56,57,58,59,60}, + new int[] {3,4,5,6,13,15,16,17,18,19,21,22,24,25,33,34,35,36,37,39,40,41,42,43,44,45,47,52,53,55,57,58,59,60}, + new int[] {4,5,6,8,11,12,13,14,17,19,20,22,23,24,25,33,34,35,36,37,39,40,41,42,43,47,48,49,50,51,52,55,56,57}, + new int[] {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {12,25,33,35,36,37,39,41,42,43,48,51,52,53,54,57,59,60}, + new int[] {4,8,16,17,19,23,25,33,34,35,37,41,44,45,47,48,49,50}, + new int[] {3,23,24,25,33,35,36,37,38,39,40,42,43,44,49,50,53,54,55,56,57,58,60}, + new int[] {7,13,19,20,22,23,24,25,33,34,35,38,40,41,42,44,45,46,47,48,49,52,53,54,58,59,60} + }; + + // how long each talk lasts for each speaker + int[] durations = { 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + int sum_of_durations = durations.Sum(); + + int number_of_speakers = durations.Length; + // calculate the total number of slots (maximum in the availability array) + // (and add the max durations) + int last_slot = (from s in Enumerable.Range(0, number_of_speakers) + select speaker_availability[s].Max()).Max(); + Console.WriteLine( + "Scheduling {0} speakers, for a total of {1} slots, during [{2}..{3}]", + number_of_speakers, sum_of_durations, first_slot, last_slot); + + // Start variable for all talks. + IntVar[] starts = new IntVar[number_of_speakers]; + // We store the possible starts for all talks filtered from the + // duration and the speaker availability. + int[][] possible_starts = new int[number_of_speakers][]; + + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + int duration = durations[speaker]; + // Let's filter the possible starts. + List filtered_starts = new List(); + int availability = speaker_availability[speaker].Length; + for (int index = 0; index < availability; ++index) + { + bool ok = true; + int slot = speaker_availability[speaker][index]; + if (slot < first_slot) + { + continue; + } + for (int offset = 1; offset < durations[speaker]; ++offset) + { + if (index + offset >= availability || + speaker_availability[speaker][index + offset] != slot + offset) + { + // discontinuity. + ok = false; + break; + } + } + if (ok) + { + filtered_starts.Add(slot); + } + possible_starts[speaker] = filtered_starts.ToArray(); + } + starts[speaker] = + solver.MakeIntVar(possible_starts[speaker], "start[" + speaker + "]"); + } + + List[] contributions_per_slot = + new List[last_slot + 1]; + for (int slot = first_slot; slot <= last_slot; ++slot) + { + contributions_per_slot[slot] = new List(); + } + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + int duration = durations[speaker]; + IntVar start_var = starts[speaker]; + foreach (int start in possible_starts[speaker]) + { + for (int offset = 0; offset < duration; ++offset) + { + contributions_per_slot[start + offset].Add(start_var.IsEqual(start)); + } + } + } + // Force the schedule to be consistent. + for (int slot = first_slot; slot <= last_slot; ++slot) + { + solver.Add( + solver.MakeSumLessOrEqual(contributions_per_slot[slot].ToArray(), 1)); + } + + // Add minimum start info. + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + solver.Add(starts[speaker] >= first_slot); + } + + // Creates makespan. + IntVar[] end_times = new IntVar[number_of_speakers]; + for (int speaker = 0; speaker < number_of_speakers; speaker++) + { + end_times[speaker] = (starts[speaker] + (durations[speaker] - 1)).Var(); + } + IntVar last_slot_var = end_times.Max().VarWithName("last_slot"); + + // Add trivial bound to objective. + last_slot_var.SetMin(first_slot + sum_of_durations - 1); + + // Redundant scheduling constraint. + IntervalVar[] intervals = + solver.MakeFixedDurationIntervalVarArray(starts, durations, "intervals"); + DisjunctiveConstraint disjunctive = + solver.MakeDisjunctiveConstraint(intervals, "disjunctive"); + solver.Add(disjunctive); + + // + // Search + // + List short_talks = new List(); + List long_talks = new List(); + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + if (durations[speaker] == 1) + { + short_talks.Add(starts[speaker]); + } + else + { + long_talks.Add(starts[speaker]); + } + } + OptimizeVar objective_monitor = solver.MakeMinimize(last_slot_var, 1); + DecisionBuilder long_phase = + solver.MakePhase(long_talks.ToArray(), + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + DecisionBuilder short_phase = + new FlowAssign(short_talks.ToArray(), first_slot, last_slot_var); + DecisionBuilder obj_phase = + solver.MakePhase(last_slot_var, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + DecisionBuilder main_phase = + solver.Compose(long_phase, short_phase, obj_phase); + + solver.NewSearch(main_phase, objective_monitor); + while (solver.NextSolution()) + { + Console.WriteLine("\nLast used slot: " + (last_slot_var.Value())); + Console.WriteLine("Speakers (start..end):"); + for (int s = 0; s < number_of_speakers; s++) + { + long sstart = starts[s].Value(); + Console.WriteLine(" - speaker {0,2}: {1,2}..{2,2}", (s + 1), + sstart, (sstart + durations[s] - 1)); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + solver.EndSearch(); + } + + public static void Main(String[] args) + { + int start = 1; + if (args.Length == 1) + { + start = int.Parse(args[0]); + } + Stopwatch s = new Stopwatch(); + s.Start(); + for (int i = start; i < 40; i++) + { + Solve(i); + } + + s.Stop(); + Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); + } +} diff --git a/examples/dotnet/csharp/stable_marriage.cs b/examples/dotnet/csharp/stable_marriage.cs new file mode 100644 index 0000000000..9a20b10a52 --- /dev/null +++ b/examples/dotnet/csharp/stable_marriage.cs @@ -0,0 +1,232 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class StableMarriage +{ + + /** + * + * Solves some stable marriage problems. + * See http://www.hakank.org/or-tools/stable_marriage.py + * + */ + private static void Solve(int[][][] ranks, String problem_name) + { + + Solver solver = new Solver("StableMarriage"); + + // + // data + // + int n = ranks[0].Length; + + Console.WriteLine("\n#####################"); + Console.WriteLine("Problem: " + problem_name); + + int[][] rankWomen = ranks[0]; + int[][] rankMen = ranks[1]; + + // + // Decision variables + // + IntVar[] wife = solver.MakeIntVarArray(n, 0, n - 1, "wife"); + IntVar[] husband = solver.MakeIntVarArray(n, 0, n - 1, "husband"); + + // + // Constraints + // + // (The comments below are the Comet code) + + // + // forall(m in Men) + // cp.post(husband[wife[m]] == m); + for(int m = 0; m < n; m++) { + solver.Add(husband.Element(wife[m]) == m); + } + + // forall(w in Women) + // cp.post(wife[husband[w]] == w); + for(int w = 0; w < n; w++) { + solver.Add(wife.Element(husband[w]) == w); + } + + + // forall(m in Men, o in Women) + // cp.post(rankMen[m,o] < rankMen[m, wife[m]] => + // rankWomen[o,husband[o]] < rankWomen[o,m]); + for(int m = 0; m < n; m++) { + for(int o = 0; o < n; o++) { + IntVar b1 = rankMen[m].Element(wife[m]) > rankMen[m][o]; + IntVar b2 = rankWomen[o].Element(husband[o]) < rankWomen[o][m]; + solver.Add(b1 <= b2); + } + + } + + // forall(w in Women, o in Men) + // cp.post(rankWomen[w,o] < rankWomen[w,husband[w]] => + // rankMen[o,wife[o]] < rankMen[o,w]); + for(int w = 0; w < n; w++) { + for(int o = 0; o < n; o++) { + IntVar b1 = rankWomen[w].Element(husband[w]) > rankWomen[w][o]; + IntVar b2 = rankMen[o].Element(wife[o]) < rankMen[o][w]; + solver.Add(b1 <= b2); + } + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(wife, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("wife : "); + for(int i = 0; i < n; i++) { + Console.Write(wife[i].Value() + " "); + } + Console.Write("\nhusband: "); + for(int i = 0; i < n; i++) { + Console.Write(husband[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + // + // From Pascal Van Hentenryck's OPL book + // + int[][][] van_hentenryck = { + // rankWomen + new int[][] { + new int[] {1, 2, 4, 3, 5}, + new int[] {3, 5, 1, 2, 4}, + new int[] {5, 4, 2, 1, 3}, + new int[] {1, 3, 5, 4, 2}, + new int[] {4, 2, 3, 5, 1}}, + + // rankMen + new int[][] { + new int[] {5, 1, 2, 4, 3}, + new int[] {4, 1, 3, 2, 5}, + new int[] {5, 3, 2, 4, 1}, + new int[] {1, 5, 4, 3, 2}, + new int[] {4, 3, 2, 1, 5}} + }; + + // + // Data from MathWorld + // http://mathworld.wolfram.com/StableMarriageProblem.html + // + int[][][] mathworld = { + // rankWomen + new int[][] { + new int[] {3, 1, 5, 2, 8, 7, 6, 9, 4}, + new int[] {9, 4, 8, 1, 7, 6, 3, 2, 5}, + new int[] {3, 1, 8, 9, 5, 4, 2, 6, 7}, + new int[] {8, 7, 5, 3, 2, 6, 4, 9, 1}, + new int[] {6, 9, 2, 5, 1, 4, 7, 3, 8}, + new int[] {2, 4, 5, 1, 6, 8, 3, 9, 7}, + new int[] {9, 3, 8, 2, 7, 5, 4, 6, 1}, + new int[] {6, 3, 2, 1, 8, 4, 5, 9, 7}, + new int[] {8, 2, 6, 4, 9, 1, 3, 7, 5}}, + + // rankMen + new int[][] { + new int[] {7, 3, 8, 9, 6, 4, 2, 1, 5}, + new int[] {5, 4, 8, 3, 1, 2, 6, 7, 9}, + new int[] {4, 8, 3, 9, 7, 5, 6, 1, 2}, + new int[] {9, 7, 4, 2, 5, 8, 3, 1, 6}, + new int[] {2, 6, 4, 9, 8, 7, 5, 1, 3}, + new int[] {2, 7, 8, 6, 5, 3, 4, 1, 9}, + new int[] {1, 6, 2, 3, 8, 5, 4, 9, 7}, + new int[] {5, 6, 9, 1, 2, 8, 4, 3, 7}, + new int[] {6, 1, 4, 7, 5, 8, 3, 9, 2}} + }; + + // + // Data from + // http://www.csee.wvu.edu/~ksmani/courses/fa01/random/lecnotes/lecture5.pdf + // + int[][][] problem3 = { + // rankWomen + new int[][] { + new int[] {1,2,3,4}, + new int[] {4,3,2,1}, + new int[] {1,2,3,4}, + new int[] {3,4,1,2}}, + + // rankMen + new int[][] { + new int[] {1,2,3,4}, + new int[] {2,1,3,4}, + new int[] {1,4,3,2}, + new int[] {4,3,1,2}} + }; + + + // + // Data from + // http://www.comp.rgu.ac.uk/staff/ha/ZCSP/additional_problems/stable_marriage/stable_marriage.pdf + // page 4 + // + int[][][] problem4 = { + // rankWomen + new int[][] { + new int[] {1,5,4,6,2,3}, + new int[] {4,1,5,2,6,3}, + new int[] {6,4,2,1,5,3}, + new int[] {1,5,2,4,3,6}, + new int[] {4,2,1,5,6,3}, + new int[] {2,6,3,5,1,4}}, + + // rankMen + new int[][] { + new int[] {1,4,2,5,6,3}, + new int[] {3,4,6,1,5,2}, + new int[] {1,6,4,2,3,5}, + new int[] {6,5,3,4,2,1}, + new int[] {3,1,2,4,5,6}, + new int[] {2,3,1,6,5,4}}}; + + + Solve(van_hentenryck, "Van Hentenryck"); + Solve(mathworld, "MathWorld"); + Solve(problem3, "Problem 3"); + Solve(problem4, "Problem 4"); + + } +} diff --git a/examples/dotnet/csharp/strimko2.cs b/examples/dotnet/csharp/strimko2.cs new file mode 100644 index 0000000000..4b2c767019 --- /dev/null +++ b/examples/dotnet/csharp/strimko2.cs @@ -0,0 +1,133 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Strimko2 +{ + + /** + * + * Solves a Strimko problem. + * See http://www.hakank.org/google_or_tools/strimko2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Strimko2"); + + // + // data + // + int[,] streams = {{1,1,2,2,2,2,2}, + {1,1,2,3,3,3,2}, + {1,4,1,3,3,5,5}, + {4,4,3,1,3,5,5}, + {4,6,6,6,7,7,5}, + {6,4,6,4,5,5,7}, + {6,6,4,7,7,7,7}}; + + // Note: This is 1-based + int[,] placed = {{2,1,1}, + {2,3,7}, + {2,5,6}, + {2,7,4}, + {3,2,7}, + {3,6,1}, + {4,1,4}, + {4,7,5}, + {5,2,2}, + {5,6,6}}; + + int n = streams.GetLength(0); + int num_placed = placed.GetLength(0); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + // all rows and columns must be unique, i.e. a Latin Square + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + col[j] = x[j,i]; + } + + solver.Add(row.AllDifferent()); + solver.Add(col.AllDifferent()); + } + + // streams + for(int s = 1; s <= n; s++) { + IntVar[] tmp = (from i in Enumerable.Range(0, n) + from j in Enumerable.Range(0, n) + where streams[i,j] == s + select x[i,j]).ToArray(); + solver.Add(tmp.AllDifferent()); + + } + + // placed + for(int i = 0; i < num_placed; i++) { + // note: also adjust to 0-based + solver.Add(x[placed[i,0] - 1,placed[i,1] - 1] == placed[i,2]); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/subset_sum.cs b/examples/dotnet/csharp/subset_sum.cs new file mode 100644 index 0000000000..8d223df732 --- /dev/null +++ b/examples/dotnet/csharp/subset_sum.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class SubsetSum +{ + + public static IntVar[] subset_sum(Solver solver, + int[] values, + int total) { + int n = values.Length; + IntVar[] x = solver.MakeIntVarArray(n, 0, n, "x"); + solver.Add(x.ScalProd(values) == total); + + return x; + } + + + /** + * + * Subset sum problem. + * + * From Katta G. Murty: 'Optimization Models for Decision Making', page 340 + * http://ioe.engin.umich.edu/people/fac/books/murty/opti_model/junior-7.pdf + * """ + * Example 7.8.1 + * + * A bank van had several bags of coins, each containing either + * 16, 17, 23, 24, 39, or 40 coins. While the van was parked on the + * street, thieves stole some bags. A total of 100 coins were lost. + * It is required to find how many bags were stolen. + * """ + * + * Also see http://www.hakank.org/or-tools/subset_sum.py + * + */ + private static void Solve(int[] coins, int total) + { + + Solver solver = new Solver("SubsetSum"); + + int n = coins.Length; + Console.Write("Coins: "); + for(int i = 0; i < n; i++) { + Console.Write(coins[i] + " "); + } + Console.WriteLine("\nTotal: {0}", total); + + // + // Variables + // + // number of coins + IntVar s = solver.MakeIntVar(0, coins.Sum(), "s"); + + + // + // Constraints + // + IntVar[] x = subset_sum(solver, coins, total); + solver.Add(x.Sum() == s); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(" s: {0}", s.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int[] coins = {16, 17, 23, 24, 39, 40}; + int total = 100; + + if (args.Length > 0) { + total = Convert.ToInt32(args[0]); + } + + Solve(coins, total); + } +} diff --git a/examples/dotnet/csharp/sudoku.cs b/examples/dotnet/csharp/sudoku.cs new file mode 100644 index 0000000000..d57b3bcd19 --- /dev/null +++ b/examples/dotnet/csharp/sudoku.cs @@ -0,0 +1,133 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Sudoku +{ + + /** + * + * Solves a Sudoku problem. + * + */ + private static void Solve() + { + Solver solver = new Solver("Sudoku"); + + // + // data + // + int cell_size = 3; + IEnumerable CELL = Enumerable.Range(0, cell_size); + int n = cell_size * cell_size; + IEnumerable RANGE = Enumerable.Range(0, n); + + // 0 marks an unknown value + int[,] initial_grid = {{0, 6, 0, 0, 5, 0, 0, 2, 0}, + {0, 0, 0, 3, 0, 0, 0, 9, 0}, + {7, 0, 0, 6, 0, 0, 0, 1, 0}, + {0, 0, 6, 0, 3, 0, 4, 0, 0}, + {0, 0, 4, 0, 7, 0, 1, 0, 0}, + {0, 0, 5, 0, 9, 0, 8, 0, 0}, + {0, 4, 0, 0, 0, 1, 0, 0, 6}, + {0, 3, 0, 0, 0, 8, 0, 0, 0}, + {0, 2, 0, 0, 4, 0, 0, 5, 0}}; + + + // + // Decision variables + // + IntVar[,] grid = solver.MakeIntVarMatrix(n, n, 1, 9, "grid"); + IntVar[] grid_flat = grid.Flatten(); + + // + // Constraints + // + + // init + foreach(int i in RANGE) { + foreach(int j in RANGE) { + if (initial_grid[i,j] > 0) { + solver.Add(grid[i,j] == initial_grid[i,j]); + } + } + } + + + foreach(int i in RANGE) { + + // rows + solver.Add( (from j in RANGE + select grid[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE + select grid[j,i]).ToArray().AllDifferent()); + + } + + // cells + foreach(int i in CELL) { + foreach(int j in CELL) { + solver.Add( (from di in CELL + from dj in CELL + select grid[i*cell_size+di, j*cell_size+dj] + ).ToArray().AllDifferent()); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(grid_flat, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_SIMPLE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", grid[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/survo_puzzle.cs b/examples/dotnet/csharp/survo_puzzle.cs new file mode 100644 index 0000000000..645ec77951 --- /dev/null +++ b/examples/dotnet/csharp/survo_puzzle.cs @@ -0,0 +1,238 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class SurvoPuzzle +{ + + // + // default problem + // + static int default_r = 3; + static int default_c = 4; + static int[] default_rowsums = {30, 18, 30}; + static int[] default_colsums = {27, 16, 10, 25}; + static int[,] default_game = {{0, 6, 0, 0}, + {8, 0, 0, 0}, + {0, 0, 3, 0}}; + + + // for the actual problem + static int r; + static int c; + static int[] rowsums; + static int[] colsums; + static int[,] game; + + + + /** + * + * Solves the Survo puzzle. + * See http://www.hakank.org/or-tools/survo_puzzle.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SurvoPuzzle"); + + // + // data + // + Console.WriteLine("Problem:"); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + Console.Write(game[i,j] + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(r, c, 1, r*c, "x"); + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] > 0) { + solver.Add(x[i,j] == game[i,j]); + } + } + } + + solver.Add(x_flat.AllDifferent()); + + + // + // calculate rowsums and colsums + // + for(int i = 0; i < r; i++) { + IntVar[] row = new IntVar[c]; + for(int j = 0; j < c; j++) { + row[j] = x[i,j]; + } + solver.Add(row.Sum() == rowsums[i]); + } + + for(int j = 0; j < c; j++) { + IntVar[] col = new IntVar[r]; + for(int i = 0; i < r; i++) { + col[i] = x[i,j]; + } + solver.Add(col.Sum() == colsums[j]); + + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_SIMPLE, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * readFile() + * + * Reads a Survo puzzle in the following format + * r + * c + * rowsums + * olsums + * data + * ... + * + * Example: + * 3 + * 4 + * 30,18,30 + * 27,16,10,25 + * 0,6,0,0 + * 8,0,0,0 + * 0,0,3,0 + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + + r = Convert.ToInt32(inr.ReadLine()); + c = Convert.ToInt32(inr.ReadLine()); + rowsums = new int[r]; + colsums = new int[c]; + Console.WriteLine("r: " + r + " c: " + c); + + String[] rowsums_str = Regex.Split(inr.ReadLine(),",\\s*"); + String[] colsums_str = Regex.Split(inr.ReadLine(),",\\s*"); + Console.WriteLine("rowsums:"); + for(int i = 0; i < r; i++) { + Console.Write(rowsums_str[i] + " "); + rowsums[i] = Convert.ToInt32(rowsums_str[i]); + } + Console.WriteLine("\ncolsums:"); + for(int j = 0; j < c; j++) { + Console.Write(colsums_str[j] + " "); + colsums[j] = Convert.ToInt32(colsums_str[j]); + } + Console.WriteLine(); + + // init the game matrix and read data from file + game = new int[r,c]; + String str; + int line_count = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + // ignore comments + // starting with either # or % + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + String[] this_row = Regex.Split(str, ",\\s*"); + for(int j = 0; j < this_row.Length; j++) { + game[line_count,j] = Convert.ToInt32(this_row[j]); + } + + line_count++; + + } // end while + + inr.Close(); + + + } // end readFile + + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + r = default_r; + c = default_c; + game = default_game; + rowsums = default_rowsums; + colsums = default_colsums; + } + + Solve(); + } +} diff --git a/examples/dotnet/csharp/techtalk_scheduling.cs b/examples/dotnet/csharp/techtalk_scheduling.cs new file mode 100644 index 0000000000..9bd6bb7667 --- /dev/null +++ b/examples/dotnet/csharp/techtalk_scheduling.cs @@ -0,0 +1,282 @@ +// Copyright 2010-2017 Google +// 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. + +using Google.OrTools.ConstraintSolver; +using Google.OrTools.Graph; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System; + +public class SpeakerScheduling +{ + + public class FlowAssign : NetDecisionBuilder + { + public FlowAssign(IntVar[] vars, int first_slot, IntVar last_slot_var) + { + vars_ = vars; + first_slot_ = first_slot; + last_slot_var_ = last_slot_var; + } + + public override Decision Next(Solver solver) + { + int large = 100000; + int number_of_variables = vars_.Length; + long last_slot = last_slot_var_.Max(); + // Lets build a bipartite graph with equal number of nodes left and right. + // Variables will be on the left, slots on the right. + // We will add dummy variables when needed. + // Arcs will have a cost x is slot x is possible for a variable, a large + // number otherwise. For dummy variables, the cost will be 0 always. + LinearSumAssignment matching = new LinearSumAssignment(); + for (int speaker = 0; speaker < number_of_variables; ++speaker) + { + IntVar var = vars_[speaker]; + for (int value = first_slot_; value <= last_slot; ++value) + { + if (var.Contains(value)) + { + matching.AddArcWithCost(speaker, value - first_slot_, value); + } + else + { + matching.AddArcWithCost(speaker, value - first_slot_, large); + } + } + } + // The Matching algorithms expect the same number of left and right nodes. + // So we fill the rest with dense zero-cost arcs. + for (int dummy = number_of_variables; + dummy <= last_slot - first_slot_; ++dummy) { + for (int value = first_slot_; value <= last_slot; ++value) + { + matching.AddArcWithCost(dummy, value - first_slot_, 0); + } + } + if (matching.Solve() == LinearSumAssignment.OPTIMAL && + matching.OptimalCost() < large) // No violated arcs. + { + for (int speaker = 0; speaker < number_of_variables; ++speaker) + { + vars_[speaker].SetValue(matching.RightMate(speaker) + first_slot_); + } + } else { + solver.Fail(); + } + return null; + } + + private IntVar[] vars_; + private int first_slot_; + private IntVar last_slot_var_; + } + + private static void Solve(int first_slot) + { + Console.WriteLine("----------------------------------------------------"); + Solver solver = new Solver("SpeakerScheduling"); + + // the slots each speaker is available + int[][] speaker_availability = { + new int[] {1,3,4,6,7,10,12,13,14,15,16,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59}, + new int[] {1,2,7,8,10,11,16,17,18,21,22,23,24,25,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,52,53,54,55,56,57,58,59,60}, + new int[] {5,6,7,10,12,14,16,17,21,22,23,24,33,35,37,38,39,40,41,42,43,44,45,46,51,53,55,56,57,58,59}, + new int[] {1,2,3,4,5,6,7,11,13,14,15,16,20,24,25,33,34,35,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,60}, + new int[] {4,7,8,9,16,17,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,49,50,51,53,55,56,57,58,59,60}, + new int[] {4,7,9,11,12,13,14,15,16,17,18,22,23,24,33,34,35,36,38,39,42,44,46,48,49,51,53,54,55,56,57}, + new int[] {1,2,3,4,5,6,7,33,34,35,36,37,38,39,40,41,42,54,55,56,57,58,59,60}, + new int[] {1,3,11,14,15,16,17,21,22,23,24,25,33,35,36,37,39,40,41,42,43,44,45,47,48,49,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,7,8,9,10,11,13,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,50,51,52,53,54,55,56,57,59,60}, + new int[] {24,33,34,35,36,37,38,39,40,41,42,43,45,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,51,52,53,55,56,57,58,59,60}, + new int[] {3,4,5,6,13,15,16,17,18,19,21,22,24,25,33,34,35,36,37,39,40,41,42,43,44,45,47,52,53,55,57,58,59,60}, + new int[] {4,5,6,8,11,12,13,14,17,19,20,22,23,24,25,33,34,35,36,37,39,40,41,42,43,47,48,49,50,51,52,55,56,57}, + new int[] {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {12,25,33,35,36,37,39,41,42,43,48,51,52,53,54,57,59,60}, + new int[] {4,8,16,17,19,23,25,33,34,35,37,41,44,45,47,48,49,50}, + new int[] {3,23,24,25,33,35,36,37,38,39,40,42,43,44,49,50,53,54,55,56,57,58,60}, + new int[] {7,13,19,20,22,23,24,25,33,34,35,38,40,41,42,44,45,46,47,48,49,52,53,54,58,59,60} + }; + + // how long each talk lasts for each speaker + int[] durations = { 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + int sum_of_durations = durations.Sum(); + + int number_of_speakers = durations.Length; + // calculate the total number of slots (maximum in the availability array) + // (and add the max durations) + int last_slot = (from s in Enumerable.Range(0, number_of_speakers) + select speaker_availability[s].Max()).Max(); + Console.WriteLine( + "Scheduling {0} speakers, for a total of {1} slots, during [{2}..{3}]", + number_of_speakers, sum_of_durations, first_slot, last_slot); + + // Start variable for all talks. + IntVar[] starts = new IntVar[number_of_speakers]; + // We store the possible starts for all talks filtered from the + // duration and the speaker availability. + int[][] possible_starts = new int[number_of_speakers][]; + + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + int duration = durations[speaker]; + // Let's filter the possible starts. + List filtered_starts = new List(); + int availability = speaker_availability[speaker].Length; + for (int index = 0; index < availability; ++index) + { + bool ok = true; + int slot = speaker_availability[speaker][index]; + if (slot < first_slot) + { + continue; + } + for (int offset = 1; offset < duration; ++offset) + { + if (index + offset >= availability || + speaker_availability[speaker][index + offset] != slot + offset) + { + // discontinuity. + ok = false; + break; + } + } + if (ok) + { + filtered_starts.Add(slot); + } + possible_starts[speaker] = filtered_starts.ToArray(); + } + starts[speaker] = + solver.MakeIntVar(possible_starts[speaker], "start[" + speaker + "]"); + } + + List[] contributions_per_slot = + new List[last_slot + 1]; + for (int slot = first_slot; slot <= last_slot; ++slot) + { + contributions_per_slot[slot] = new List(); + } + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + int duration = durations[speaker]; + IntVar start_var = starts[speaker]; + foreach (int start in possible_starts[speaker]) + { + for (int offset = 0; offset < duration; ++offset) + { + contributions_per_slot[start + offset].Add(start_var.IsEqual(start)); + } + } + } + // Force the schedule to be consistent. + for (int slot = first_slot; slot <= last_slot; ++slot) + { + solver.Add( + solver.MakeSumLessOrEqual(contributions_per_slot[slot].ToArray(), 1)); + } + + // Add minimum start info. + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + solver.Add(starts[speaker] >= first_slot); + } + + // Creates makespan. + IntVar[] end_times = new IntVar[number_of_speakers]; + for (int speaker = 0; speaker < number_of_speakers; speaker++) + { + end_times[speaker] = (starts[speaker] + (durations[speaker] - 1)).Var(); + } + IntVar last_slot_var = end_times.Max().VarWithName("last_slot"); + + // Add trivial bound to objective. + last_slot_var.SetMin(first_slot + sum_of_durations - 1); + + // Redundant scheduling constraint. + IntervalVar[] intervals = + solver.MakeFixedDurationIntervalVarArray(starts, durations, "intervals"); + DisjunctiveConstraint disjunctive = + solver.MakeDisjunctiveConstraint(intervals, "disjunctive"); + solver.Add(disjunctive); + + // + // Search + // + List short_talks = new List(); + List long_talks = new List(); + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + if (durations[speaker] == 1) + { + short_talks.Add(starts[speaker]); + } + else + { + long_talks.Add(starts[speaker]); + } + } + OptimizeVar objective_monitor = solver.MakeMinimize(last_slot_var, 1); + DecisionBuilder long_phase = + solver.MakePhase(long_talks.ToArray(), + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + DecisionBuilder short_phase = + new FlowAssign(short_talks.ToArray(), first_slot, last_slot_var); + DecisionBuilder obj_phase = + solver.MakePhase(last_slot_var, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + DecisionBuilder main_phase = + solver.Compose(long_phase, short_phase, obj_phase); + + solver.NewSearch(main_phase, objective_monitor); + while (solver.NextSolution()) + { + Console.WriteLine("\nLast used slot: " + (last_slot_var.Value())); + Console.WriteLine("Speakers (start..end):"); + for (int s = 0; s < number_of_speakers; s++) + { + long sstart = starts[s].Value(); + Console.WriteLine(" - speaker {0,2}: {1,2}..{2,2}", (s + 1), + sstart, (sstart + durations[s] - 1)); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + solver.EndSearch(); + } + + public static void Main(String[] args) + { + int start = 1; + if (args.Length == 1) + { + start = int.Parse(args[0]); + } + Stopwatch s = new Stopwatch(); + s.Start(); + for (int i = start; i < 40; i++) + { + Solve(i); + } + + s.Stop(); + Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); + } +} diff --git a/examples/dotnet/csharp/to_num.cs b/examples/dotnet/csharp/to_num.cs new file mode 100644 index 0000000000..76ff19c649 --- /dev/null +++ b/examples/dotnet/csharp/to_num.cs @@ -0,0 +1,101 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class ToNumTest +{ + + + /** + * + * toNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + + /** + * + * Implements toNum: channeling between a number and an array. + * See http://www.hakank.org/or-tools/toNum.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ToNum"); + + int n = 5; + int bbase = 10; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, bbase - 1, "x"); + IntVar num = solver.MakeIntVar(0, (int)Math.Pow(bbase, n) - 1, "num"); + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + solver.Add(ToNum(x, num, bbase)); + + // extra constraint (just for fun) + // second digit should be 7 + // solver.Add(x[1] == 7); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("\n" + num.Value() + ": "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/traffic_lights.cs b/examples/dotnet/csharp/traffic_lights.cs new file mode 100644 index 0000000000..615f6ba043 --- /dev/null +++ b/examples/dotnet/csharp/traffic_lights.cs @@ -0,0 +1,142 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class TrafficLights +{ + + /** + * + * Traffic lights problem. + * + * CSPLib problem 16 + * http://www.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob016/index.html + * """ + * Specification: + * Consider a four way traffic junction with eight traffic lights. Four of the traffic + * lights are for the vehicles and can be represented by the variables V1 to V4 with domains + * {r,ry,g,y} (for red, red-yellow, green and yellow). The other four traffic lights are + * for the pedestrians and can be represented by the variables P1 to P4 with domains {r,g}. + * + * The constraints on these variables can be modelled by quaternary constraints on + * (Vi, Pi, Vj, Pj ) for 1<=i<=4, j=(1+i)mod 4 which allow just the tuples + * {(r,r,g,g), (ry,r,y,r), (g,g,r,r), (y,r,ry,r)}. + * + * It would be interesting to consider other types of junction (e.g. five roads + * intersecting) as well as modelling the evolution over time of the traffic light sequence. + * ... + * + * Results + * Only 2^2 out of the 2^12 possible assignments are solutions. + * + * (V1,P1,V2,P2,V3,P3,V4,P4) = + * {(r,r,g,g,r,r,g,g), (ry,r,y,r,ry,r,y,r), (g,g,r,r,g,g,r,r), (y,r,ry,r,y,r,ry,r)} + * [(1,1,3,3,1,1,3,3), ( 2,1,4,1, 2,1,4,1), (3,3,1,1,3,3,1,1), (4,1, 2,1,4,1, 2,1)} + * The problem has relative few constraints, but each is very + * tight. Local propagation appears to be rather ineffective on this + * problem. + * + * """ + * Note: In this model we use only the constraint + * solver.AllowedAssignments(). + * + * + * See http://www.hakank.org/or-tools/traffic_lights.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("TrafficLights"); + + // + // data + // + int n = 4; + + int r = 0; + int ry = 1; + int g = 2; + int y = 3; + + string[] lights = {"r", "ry", "g", "y"}; + + // The allowed combinations + IntTupleSet allowed = new IntTupleSet(4); + allowed.InsertAll(new int[,] {{r,r,g,g}, + {ry,r,y,r}, + {g,g,r,r}, + {y,r,ry,r}}); + // + // Decision variables + // + IntVar[] V = solver.MakeIntVarArray(n, 0, n-1, "V"); + IntVar[] P = solver.MakeIntVarArray(n, 0, n-1, "P"); + + // for search + IntVar[] VP = new IntVar[2 * n]; + for(int i = 0; i < n; i++) { + VP[i] = V[i]; + VP[i+n] = P[i]; + } + + // + // Constraints + // + for(int i = 0; i < n; i++) { + int j = (1+i) % n; + IntVar[] tmp = new IntVar[] {V[i],P[i],V[j],P[j]}; + solver.Add(tmp.AllowedAssignments(allowed)); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(VP, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0,2} {1,2} ", + lights[V[i].Value()], + lights[P[i].Value()]); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + + } +} diff --git a/examples/dotnet/csharp/volsay.cs b/examples/dotnet/csharp/volsay.cs new file mode 100644 index 0000000000..a04a2f44e4 --- /dev/null +++ b/examples/dotnet/csharp/volsay.cs @@ -0,0 +1,89 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + + +public class Volsay +{ + + /** + * + * Volsay problem. + * + * From the OPL model volsay.mod. + * + * + * Also see http://www.hakank.org/or-tools/volsay.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Volsay", Solver.CLP_LINEAR_PROGRAMMING); + + // + // Variables + // + + Variable Gas = solver.MakeNumVar(0, 100000, "Gas"); + Variable Chloride = solver.MakeNumVar(0, 100000, "Cloride"); + + Constraint c1 = solver.Add(Gas + Chloride <= 50); + Constraint c2 = solver.Add(3 * Gas + 4 * Chloride <= 180); + + solver.Maximize(40 * Gas + 50 * Chloride); + + int resultStatus = solver.Solve(); + + if (resultStatus != Solver.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + Console.WriteLine("Objective: {0}", solver.Objective().Value()); + + Console.WriteLine("Gas : {0} ReducedCost: {1}", + Gas.SolutionValue(), + Gas.ReducedCost()); + + Console.WriteLine("Chloride : {0} ReducedCost: {1}", + Chloride.SolutionValue(), + Chloride.ReducedCost()); + + Console.WriteLine("c1 : DualValue: {0} Activity: {1}", + c1.DualValue(), + c1.Activity()); + + Console.WriteLine("c2 : DualValue: {0} Activity: {1}", + c2.DualValue(), + c2.Activity()); + + + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/volsay2.cs b/examples/dotnet/csharp/volsay2.cs new file mode 100644 index 0000000000..8b26705148 --- /dev/null +++ b/examples/dotnet/csharp/volsay2.cs @@ -0,0 +1,107 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + + +public class Volsay2 +{ + + /** + * + * Volsay problem. + * + * From the OPL model volsay.mod. + * + * + * + * Also see + * http://www.hakank.org/or-tools/volsay.cs + * http://www.hakank.org/or-tools/volsay2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Volsay2", + Solver.CLP_LINEAR_PROGRAMMING); + + + int num_products = 2; + IEnumerable PRODUCTS = Enumerable.Range(0, num_products); + int Gas = 0; + int Chloride = 1; + String[] products = {"Gas", "Chloride"}; + + // + // Variables + // + Variable[] production = new Variable[num_products]; + foreach(int p in PRODUCTS) { + production[p] = solver.MakeNumVar(0, 100000, products[p]); + } + + int num_constraints = 2; + IEnumerable CONSTRAINTS = Enumerable.Range(0, num_constraints); + Constraint[] cons = new Constraint[num_constraints]; + cons[0] = solver.Add(production[Gas] + production[Chloride] <= 50); + cons[1] = solver.Add(3 * production[Gas] + 4 * production[Chloride] <= 180); + + + solver.Maximize(40 * production[Gas] + 50 * production[Chloride]); + + Console.WriteLine("NumConstraints: {0}", solver.NumConstraints()); + + int resultStatus = solver.Solve(); + + if (resultStatus != Solver.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + foreach(int p in PRODUCTS) { + Console.WriteLine("{0,-10}: {1} ReducedCost: {2}", + products[p], + production[p].SolutionValue(), + production[p].ReducedCost()); + } + + + foreach(int c in CONSTRAINTS) { + Console.WriteLine("Constraint {0} DualValue {1} Activity: {2} lb: {3} ub: {4}", + c.ToString(), + cons[c].DualValue(), + cons[c].Activity(), + cons[c].Lb(), + cons[c].Ub() + ); + } + + + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/volsay3.cs b/examples/dotnet/csharp/volsay3.cs new file mode 100644 index 0000000000..f2310bd024 --- /dev/null +++ b/examples/dotnet/csharp/volsay3.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + + +public class Volsay3 +{ + + /** + * + * Volsay problem. + * + * From the OPL model volsay.mod. + * This version use arrays and matrices + * + * + * Also see + * http://www.hakank.org/or-tools/volsay2.cs + * http://www.hakank.org/or-tools/volsay3.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Volsay3", + Solver.CLP_LINEAR_PROGRAMMING); + + + int num_products = 2; + IEnumerable PRODUCTS = Enumerable.Range(0, num_products); + String[] products = {"Gas", "Chloride"}; + String[] components = {"nitrogen", "hydrogen", "chlorine"}; + + int[,] demand = { {1,3,0}, {1,4,1}}; + int[] profit = {30,40}; + int[] stock = {50,180,40}; + + + // + // Variables + // + Variable[] production = new Variable[num_products]; + foreach(int p in PRODUCTS) { + production[p] = solver.MakeNumVar(0, 100000, products[p]); + } + + // + // Constraints + // + int c_len = components.Length; + Constraint[] cons = new Constraint[c_len]; + for(int c = 0; c < c_len; c++) { + cons[c] = solver.Add( (from p in PRODUCTS + select (demand[p,c]*production[p])). + ToArray().Sum() <= stock[c]); + } + + // + // Objective + // + solver.Maximize( (from p in PRODUCTS + select (profit[p]*production[p])). + ToArray().Sum() + ); + + + + if (solver.Solve() != Solver.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + Console.WriteLine("Objective: {0}", solver.Objective().Value()); + foreach(int p in PRODUCTS) { + Console.WriteLine("{0,-10}: {1} ReducedCost: {2}", + products[p], + production[p].SolutionValue(), + production[p].ReducedCost()); + } + + + for(int c = 0; c < c_len; c++) { + Console.WriteLine("Constraint {0} DualValue {1} Activity: {2} lb: {3} ub: {4}", + c, + cons[c].DualValue(), + cons[c].Activity(), + cons[c].Lb(), + cons[c].Ub()); + } + + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/wedding_optimal_chart.cs b/examples/dotnet/csharp/wedding_optimal_chart.cs new file mode 100644 index 0000000000..9eecf2e473 --- /dev/null +++ b/examples/dotnet/csharp/wedding_optimal_chart.cs @@ -0,0 +1,215 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class WeddingOptimalChart +{ + /** + * + * Finding an optimal wedding seating chart. + * + * From + * Meghan L. Bellows and J. D. Luc Peterson + * "Finding an optimal seating chart for a wedding" + * http://www.improbable.com/news/2012/Optimal-seating-chart.pdf + * http://www.improbable.com/2012/02/12/finding-an-optimal-seating-chart-for-a-wedding + * + * """ + * Every year, millions of brides (not to mention their mothers, future + * mothers-in-law, and occasionally grooms) struggle with one of the + * most daunting tasks during the wedding-planning process: the + * seating chart. The guest responses are in, banquet hall is booked, + * menu choices have been made. You think the hard parts are over, + * but you have yet to embark upon the biggest headache of them all. + * In order to make this process easier, we present a mathematical + * formulation that models the seating chart problem. This model can + * be solved to find the optimal arrangement of guests at tables. + * At the very least, it can provide a starting point and hopefully + * minimize stress and arguments… + * """ + * + * + * Also see http://www.hakank.org/minizinc/wedding_optimal_chart.mzn + * + */ + private static void Solve() + { + + Solver solver = new Solver("WeddingOptimalChart"); + + // + // Data + // + + // Easy problem (from the paper) + int n = 2; // number of tables + int a = 10; // maximum number of guests a table can seat + int b = 1; // minimum number of people each guest knows at their table + + /* + // Sligthly harder problem (also from the paper) + int n = 5; // number of tables + int a = 4; // maximum number of guests a table can seat + int b = 1; // minimum number of people each guest knows at their table + */ + + // j Guest Relation + // ------------------------------------- + // 1 Deb mother of the bride + // 2 John father of the bride + // 3 Martha sister of the bride + // 4 Travis boyfriend of Martha + // 5 Allan grandfather of the bride + // 6 Lois wife of Allan + // 7 Jayne aunt of the bride + // 8 Brad uncle of the bride + // 9 Abby cousin of the bride + // 10 Mary Helen mother of the groom + // 11 Lee father of the groom + // 12 Annika sister of the groom + // 13 Carl brother of the groom + // 14 Colin brother of the groom + // 15 Shirley grandmother of the groom + // 16 DeAnn aunt of the groom + // 17 Lori aunt of the groom + + // Connection matrix: who knows who, and how strong + // is the relation + int[,] C = { + { 1,50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + {50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1,50, 1, 1, 1, 1,10, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1,50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1,50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1,50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1, 1, 1,50, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1, 1,50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1,10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,50, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0,50, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + // Names of the guests. B: Bride side, G: Groom side + String[] names = {"Deb (B)", + "John (B)", + "Martha (B)", + "Travis (B)", + "Allan (B)", + "Lois (B)", + "Jayne (B)", + "Brad (B)", + "Abby (B)", + "Mary Helen (G)", + "Lee (G)", + "Annika (G)", + "Carl (G)", + "Colin (G)", + "Shirley (G)", + "DeAnn (G)", + "Lori (G)"}; + + + int m = C.GetLength(0); // number of quests + + IEnumerable NRANGE = Enumerable.Range(0, n); + IEnumerable MRANGE = Enumerable.Range(0, m); + + + // + // Decision variables + // + IntVar[] tables = solver.MakeIntVarArray(m, 0, n-1, "tables"); + IntVar z = (from j in MRANGE + from k in MRANGE + where j < k + select C[j,k] * tables[j] == tables[k] + ).ToArray().Sum().VarWithName("z"); + + // + // Constraints + // + foreach(int i in NRANGE) { + + solver.Add((from j in MRANGE + from k in MRANGE + where j < k && C[j, k] > 0 + select (tables[j] == i) * (tables[k] == i) + ).ToArray().Sum() >= b); + + + solver.Add((from j in MRANGE select tables[j] == i).ToArray().Sum() <= a); + } + + // Symmetry breaking + solver.Add(tables[0] == 0); + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(tables, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + Console.Write("Table: "); + foreach(int j in MRANGE) { + Console.Write(tables[j].Value() + " "); + } + Console.WriteLine(); + + foreach(int i in NRANGE) { + Console.Write("Table {0}: ", i); + foreach(int j in MRANGE) { + if (tables[j].Value() == i) { + Console.Write(names[j] + " "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/who_killed_agatha.cs b/examples/dotnet/csharp/who_killed_agatha.cs new file mode 100644 index 0000000000..ca2bc76045 --- /dev/null +++ b/examples/dotnet/csharp/who_killed_agatha.cs @@ -0,0 +1,154 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; +using System.Collections; +using System.Linq; + +public class WhoKilledAgatha +{ + + /** + * + * Implements the Who killed Agatha problem. + * See http://www.hakank.org/google_or_tools/who_killed_agatha.py + * + */ + private static void Solve() + { + Solver solver = new Solver("WhoKilledAgatha"); + + int n = 3; + int agatha = 0; + int butler = 1; + int charles = 2; + + // + // Decision variables + // + IntVar the_killer = solver.MakeIntVar(0, 2, "the_killer"); + IntVar the_victim = solver.MakeIntVar(0, 2, "the_victim"); + IntVar[,] hates = solver.MakeIntVarMatrix(n, n, 0, 1, "hates"); + IntVar[] hates_flat = hates.Flatten(); + IntVar[,] richer = solver.MakeIntVarMatrix(n, n, 0, 1, "richer"); + IntVar[] richer_flat = richer.Flatten(); + + IntVar[] all = new IntVar[2 * n * n]; // for branching + for(int i = 0; i < n*n; i++) { + all[i] = hates_flat[i]; + all[(n*n)+i] = richer_flat[i]; + } + + // + // Constraints + // + + // Agatha, the butler, and Charles live in Dreadsbury Mansion, and + // are the only ones to live there. + + // A killer always hates, and is no richer than his victim. + // hates[the_killer, the_victim] == 1 + // hates_flat[the_killer * n + the_victim] == 1 + solver.Add(hates_flat.Element(the_killer * n + the_victim) == 1); + + // richer[the_killer, the_victim] == 0 + solver.Add(richer_flat.Element(the_killer * n + the_victim) == 0); + + // define the concept of richer: + // no one is richer than him-/herself... + for(int i = 0; i < n; i++) { + solver.Add(richer[i,i] == 0); + } + + // (contd...) if i is richer than j then j is not richer than i + // if (i != j) => + // ((richer[i,j] = 1) <=> (richer[j,i] = 0)) + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i != j) { + solver.Add((richer[i, j]==1) - (richer[j, i]==0) == 0); + } + } + } + + // Charles hates no one that Agatha hates. + // forall i in 0..2: + // (hates[agatha, i] = 1) => (hates[charles, i] = 0) + for(int i = 0; i < n; i++) { + solver.Add((hates[agatha,i]==1) - (hates[charles,i]==0) <= 0); + + } + + // Agatha hates everybody except the butler. + solver.Add(hates[agatha,charles] == 1); + solver.Add(hates[agatha,agatha] == 1); + solver.Add(hates[agatha,butler] == 0); + + // The butler hates everyone not richer than Aunt Agatha. + // forall i in 0..2: + // (richer[i, agatha] = 0) => (hates[butler, i] = 1) + for(int i = 0; i < n; i++) { + solver.Add((richer[i,agatha] == 0)-(hates[butler,i] == 1)<=0); + } + + // The butler hates everyone whom Agatha hates. + // forall i : 0..2: + // (hates[agatha, i] = 1) => (hates[butler, i] = 1) + for(int i = 0; i < n; i++) { + solver.Add((hates[agatha,i] == 1)-(hates[butler,i] == 1)<=0); + } + + // Noone hates everyone. + // forall i in 0..2: + // (sum j in 0..2: hates[i,j]) <= 2 + for(int i = 0; i < n; i++) { + solver.Add((from j in Enumerable.Range(0, n) + select hates[i,j] + ).ToArray().Sum() <= 2 ); + } + + + // Who killed Agatha? + solver.Add(the_victim == agatha); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("the_killer: " + the_killer.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/word_square.cs b/examples/dotnet/csharp/word_square.cs new file mode 100644 index 0000000000..1bbdd4cf7a --- /dev/null +++ b/examples/dotnet/csharp/word_square.cs @@ -0,0 +1,199 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class WordSquare +{ + + /** + * + * Word square. + * + * From http://en.wikipedia.org/wiki/Word_square + * """ + * A word square is a special case of acrostic. It consists of a set of words, + * all having the same number of letters as the total number of words (the + * 'order' of the square); when the words are written out in a square grid + * horizontally, the same set of words can be read vertically. + * """ + * + * See http://www.hakank.org/or-tools/word_square.py + * + */ + private static void Solve(String[] words, int word_len, int num_answers) + { + + Solver solver = new Solver("WordSquare"); + + int num_words = words.Length; + Console.WriteLine("num_words: " + num_words); + int n = word_len; + IEnumerable WORDLEN = Enumerable.Range(0, word_len); + + // + // convert a character to integer + // + + String alpha = "abcdefghijklmnopqrstuvwxyz"; + Hashtable d = new Hashtable(); + Hashtable rev = new Hashtable(); + int count = 1; + for(int a = 0; a < alpha.Length; a++) { + d[alpha[a]] = count; + rev[count] = a; + count++; + } + + int num_letters = alpha.Length; + + // + // Decision variables + // + IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, + 0, num_letters, "A"); + IntVar[] A_flat = A.Flatten(); + IntVar[] E = solver.MakeIntVarArray(n, 0, num_words, "E"); + + + + // + // Constraints + // + solver.Add(E.AllDifferent()); + + // copy the words to a matrix + for(int i = 0; i < num_words; i++) { + char[] s = words[i].ToArray(); + foreach(int j in WORDLEN) { + int t = (int)d[s[j]]; + solver.Add(A[i,j] == t); + } + } + + foreach(int i in WORDLEN) { + foreach(int j in WORDLEN) { + solver.Add(A_flat.Element(E[i]*word_len+j) == + A_flat.Element(E[j]*word_len+i)); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(E.Concat(A_flat).ToArray(), + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_sols = 0; + while (solver.NextSolution()) { + num_sols++; + for(int i = 0; i < n; i++) { + Console.WriteLine(words[E[i].Value()] + " "); + } + Console.WriteLine(); + + if (num_answers > 0 && num_sols >= num_answers) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /* + * + * Read the words from a word list with a specific word length. + * + */ + public static String[] ReadWords(String word_list, int word_len) + { + + Console.WriteLine("ReadWords {0} {1}", word_list, word_len); + List all_words = new List(); + + TextReader inr = new StreamReader(word_list); + String str; + int count = 0; + Hashtable d = new Hashtable(); + while ((str = inr.ReadLine()) != null) { + str = str.Trim().ToLower(); + // skip weird words + if(Regex.Match(str, @"[^a-z]").Success + || + d.Contains(str) + || + str.Length == 0 + || + str.Length != word_len + ) { + continue; + } + + d[str] = 1; + all_words.Add(str); + count++; + + + } // end while + + inr.Close(); + + return all_words.ToArray(); + + } + + + public static void Main(String[] args) + { + + String word_list = "/usr/share/dict/words"; + int word_len = 4; + int num_answers = 20; + + if (args.Length > 0) { + word_list = args[0]; + } + + if (args.Length > 1) { + word_len = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + num_answers = Convert.ToInt32(args[2]); + } + + String[] words = ReadWords(word_list, word_len); + + Solve(words, word_len, num_answers); + } +} diff --git a/examples/dotnet/csharp/xkcd.cs b/examples/dotnet/csharp/xkcd.cs new file mode 100644 index 0000000000..5ab7b29d94 --- /dev/null +++ b/examples/dotnet/csharp/xkcd.cs @@ -0,0 +1,77 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using Google.OrTools.ConstraintSolver; + +public class Xkcd +{ + /** + * + * Solve the xkcd problem + * See http://www.hakank.org/google_or_tools/xkcd.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Xkcd"); + + // + // Constants, inits + // + int n = 6; + // for price and total: multiplied by 100 to be able to use integers + int[] price = {215, 275, 335, 355, 420, 580}; + int total = 1505; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 10, "x"); + + // + // Constraints + // + solver.Add(x.ScalProd(price) == total); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0}", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/examples/dotnet/csharp/young_tableaux.cs b/examples/dotnet/csharp/young_tableaux.cs new file mode 100644 index 0000000000..bfa3404cb3 --- /dev/null +++ b/examples/dotnet/csharp/young_tableaux.cs @@ -0,0 +1,141 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class YoungTableaux +{ + + + /** + * + * Implements Young tableaux and partitions. + * See http://www.hakank.org/or-tools/young_tableuax.py + * + */ + private static void Solve(int n) + { + Solver solver = new Solver("YoungTableaux"); + + // + // data + // + Console.WriteLine("n: {0}\n", n); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n + 1, "x"); + IntVar[] x_flat = x.Flatten(); + + // partition structure + IntVar[] p = solver.MakeIntVarArray(n, 0, n + 1, "p"); + + // + // Constraints + // + // 1..n is used exactly once + for(int i = 1; i <= n; i++) { + solver.Add(x_flat.Count(i, 1)); + } + + solver.Add(x[0,0] == 1); + + // row wise + for(int i = 0; i < n; i++) { + for(int j = 1; j < n; j++) { + solver.Add(x[i,j] >= x[i,j - 1]); + } + } + + // column wise + for(int j = 0; j < n; j++) { + for(int i = 1; i < n; i++) { + solver.Add(x[i,j] >= x[i - 1, j]); + } + } + + // calculate the structure (i.e. the partition) + for(int i = 0; i < n; i++) { + IntVar[] b = new IntVar[n]; + for(int j = 0; j < n; j++) { + b[j] = x[i, j] <= n; + } + solver.Add(p[i] == b.Sum()); + } + + solver.Add(p.Sum() == n); + + for(int i = 1; i < n; i++) { + solver.Add(p[i - 1] >= p[i]); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("p: "); + for(int i = 0; i < n; i++) { + Console.Write(p[i].Value() + " "); + } + Console.WriteLine("\nx:"); + + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + long val = x[i,j].Value(); + if (val <= n) { + Console.Write(val + " "); + } + } + if (p[i].Value() > 0) { + Console.WriteLine(); + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + Solve(n); + } +} diff --git a/examples/dotnet/csharp/zebra.cs b/examples/dotnet/csharp/zebra.cs new file mode 100644 index 0000000000..f1d0ba6fd5 --- /dev/null +++ b/examples/dotnet/csharp/zebra.cs @@ -0,0 +1,178 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +using System; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class NQueens +{ + /** + * + * Solves the Zebra problem. + * + * This is a port of the or-tools/Python model zebra.py + * + * + * """ + * This is the zebra problem as invented by Lewis Caroll. + * + * There are five houses. + * The Englishman lives in the red house. + * The Spaniard owns the dog. + * Coffee is drunk in the green house. + * The Ukrainian drinks tea. + * The green house is immediately to the right of the ivory house. + * The Old Gold smoker owns snails. + * Kools are smoked in the yellow house. + * Milk is drunk in the middle house. + * The Norwegian lives in the first house. + * The man who smokes Chesterfields lives in the house next to the man + * with the fox. + * Kools are smoked in the house next to the house where the horse is kept. + * The Lucky Strike smoker drinks orange juice. + * The Japanese smokes Parliaments. + * The Norwegian lives next to the blue house. + * + * Who owns a zebra and who drinks water? + * """ + * + */ + private static void Solve() + { + Solver solver = new Solver("Zebra"); + + int n = 5; + + // + // Decision variables + // + + // Colors + IntVar red = solver.MakeIntVar(1, n, "red"); + IntVar green = solver.MakeIntVar(1, n, "green"); + IntVar yellow = solver.MakeIntVar(1, n, "yellow"); + IntVar blue = solver.MakeIntVar(1, n, "blue"); + IntVar ivory = solver.MakeIntVar(1, n, "ivory"); + + // Nationality + IntVar englishman = solver.MakeIntVar(1, n, "englishman"); + IntVar spaniard = solver.MakeIntVar(1, n, "spaniard"); + IntVar japanese = solver.MakeIntVar(1, n, "japanese"); + IntVar ukrainian = solver.MakeIntVar(1, n, "ukrainian"); + IntVar norwegian = solver.MakeIntVar(1, n, "norwegian"); + + // Animal + IntVar dog = solver.MakeIntVar(1, n, "dog"); + IntVar snails = solver.MakeIntVar(1, n, "snails"); + IntVar fox = solver.MakeIntVar(1, n, "fox"); + IntVar zebra = solver.MakeIntVar(1, n, "zebra"); + IntVar horse = solver.MakeIntVar(1, n, "horse"); + + // Drink + IntVar tea = solver.MakeIntVar(1, n, "tea"); + IntVar coffee = solver.MakeIntVar(1, n, "coffee"); + IntVar water = solver.MakeIntVar(1, n, "water"); + IntVar milk = solver.MakeIntVar(1, n, "milk"); + IntVar fruit_juice = solver.MakeIntVar(1, n, "fruit juice"); + + // Smoke + IntVar old_gold = solver.MakeIntVar(1, n, "old gold"); + IntVar kools = solver.MakeIntVar(1, n, "kools"); + IntVar chesterfields = solver.MakeIntVar(1, n, "chesterfields"); + IntVar lucky_strike = solver.MakeIntVar(1, n, "lucky strike"); + IntVar parliaments = solver.MakeIntVar(1, n, "parliaments"); + + + // for search + IntVar[] all_vars = + {parliaments, kools, chesterfields, lucky_strike, old_gold, + englishman, spaniard, japanese, ukrainian, norwegian, + dog, snails, fox, zebra, horse, + tea, coffee, water, milk, fruit_juice, + red, green, yellow, blue, ivory}; + + // + // Constraints + // + + // Alldifferents + solver.Add(new IntVar[] + {red, green, yellow, blue, ivory}.AllDifferent()); + solver.Add(new IntVar[] + {englishman, spaniard, japanese, ukrainian, norwegian}.AllDifferent()); + solver.Add(new IntVar[] + {dog, snails, fox, zebra, horse}.AllDifferent()); + solver.Add(new IntVar[] + {tea, coffee, water, milk, fruit_juice}.AllDifferent()); + solver.Add(new IntVar[] + {parliaments, kools, chesterfields, lucky_strike, old_gold}.AllDifferent()); + + // + // The clues + // + solver.Add(englishman == red); + solver.Add(spaniard == dog); + solver.Add(coffee == green); + solver.Add(ukrainian == tea); + solver.Add(green == ivory + 1); + solver.Add(old_gold == snails); + solver.Add(kools == yellow); + solver.Add(milk == 3); + solver.Add(norwegian == 1); + solver.Add((fox - chesterfields).Abs() == 1); + solver.Add((horse - kools).Abs() == 1); + solver.Add(lucky_strike == fruit_juice); + solver.Add(japanese == parliaments); + solver.Add((norwegian - blue).Abs() == 1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_vars, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + IntVar[] p = {englishman, spaniard, japanese, ukrainian, norwegian}; + int[] ix = {0,1,2,3,4}; + while (solver.NextSolution()) { + int water_drinker = (from i in ix + where p[i].Value() == water.Value() + select i).First(); + int zebra_owner = (from i in ix + where p[i].Value() == zebra.Value() + select i).First(); + Console.WriteLine("The {0} drinks water.", p[water_drinker].ToString()); + Console.WriteLine("The {0} owns the zebra", p[zebra_owner].ToString()); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +}