diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index 4930f7d2ce..6cabf3ad55 100755 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -489,6 +489,9 @@ rpy_%: $(CONTRIB_EX_DIR)/%.py $(PYTHON_OR_TOOLS_LIBS) FORCE rpy_%: ortools/sat/samples/%.py $(PYTHON_OR_TOOLS_LIBS) FORCE $(SET_PYTHONPATH) "$(PYTHON_EXECUTABLE)" ortools$Ssat$Ssamples$S$*.py $(ARGS) +rpy_%: ortools/linear_solver/samples/%.py $(PYTHON_OR_TOOLS_LIBS) FORCE + $(SET_PYTHONPATH) "$(PYTHON_EXECUTABLE)" ortools$Slinear_solver$Ssamples$S$*.py $(ARGS) + .PHONY: check_python_examples # Build and Run few Python Examples (located in examples/python and examples/contrib) check_python_examples: \ rpy_simple_program \ diff --git a/ortools/linear_solver/samples/simple_lp_program.py b/ortools/linear_solver/samples/simple_lp_program.py new file mode 100644 index 0000000000..4c52c07927 --- /dev/null +++ b/ortools/linear_solver/samples/simple_lp_program.py @@ -0,0 +1,50 @@ +# 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. +"""Minimal example to call the GLOP solver.""" + +# [START program] +from __future__ import print_function + +from ortools.linear_solver import pywraplp + + +def main(): + # Create the linear solver with the GLOP backend. + solver = pywraplp.Solver('simple_lp_program', + pywraplp.Solver.GLOP_LINEAR_PROGRAMMING) + # Create the variables x and y. + x = solver.NumVar(0, 1, 'x') + y = solver.NumVar(0, 2, 'y') + + # Create a linear constraint. + ct = solver.Constraint(0, 2, 'ct') + ct.SetCoefficient(x, 1) + ct.SetCoefficient(y, 1) + + # Create the objective function, x + y. + objective = solver.Objective() + objective.SetCoefficient(x, 3) + objective.SetCoefficient(y, 1) + objective.SetMaximization() + + # Call the solver and display the results. + solver.Solve() + print('Solution:') + print('Objective value = ', objective.Value()) + print('x = ', x.solution_value()) + print('y = ', y.solution_value()) + + +if __name__ == '__main__': + main() +# [END program] diff --git a/ortools/sat/samples/jobshop.py b/ortools/sat/samples/jobshop.py index 71d6e0fcf1..ca29d4c9c3 100644 --- a/ortools/sat/samples/jobshop.py +++ b/ortools/sat/samples/jobshop.py @@ -1,9 +1,12 @@ from __future__ import print_function +import collections + # Import Python wrapper for or-tools CP-SAT solver. from ortools.sat.python import cp_model def main(): + """Minimal jobshop problem.""" # Create the model. model = cp_model.CpModel() @@ -12,92 +15,100 @@ def main(): all_machines = range(0, machines_count) all_jobs = range(0, jobs_count) # Define data. - machines = [[0, 1, 2], - [0, 2, 1], - [1, 2]] - - processing_times = [[3, 2, 2], - [2, 1, 4], - [4, 3]] + machines = [[0, 1, 2], [0, 2, 1], [1, 2]] + processing_times = [[3, 2, 2], [2, 1, 4], [4, 3]] # Computes horizon. horizon = 0 - for i in all_jobs: - horizon += sum(processing_times[i]) + for job in all_jobs: + horizon += sum(processing_times[job]) + + task_type = collections.namedtuple('task_type', 'start end interval') + assigned_task_type = collections.namedtuple('assigned_task_type', + 'start job index') # Creates jobs. all_tasks = {} - all_starts = {} - all_ends = {} - for i in all_jobs: - for j in range(0, len(machines[i])): - suffix = '%i_%i' % (i, j) - start = model.NewIntVar(0, horizon, 'start' + suffix) - end = model.NewIntVar(0, horizon, 'end' + suffix) - interval = model.NewIntervalVar(start, processing_times[i][j], end, - 'Job' + suffix) - all_starts[i, j] = start - all_ends[i, j] = end - all_tasks[i, j] = interval + for job in all_jobs: + for index in range(0, len(machines[job])): + start_var = model.NewIntVar(0, horizon, 'start_%i_%i' % (job, index)) + duration = processing_times[job][index] + end_var = model.NewIntVar(0, horizon, 'end_%i_%i' % (job, index)) + interval_var = model.NewIntervalVar(start_var, duration, end_var, + 'interval_%i_%i' % (job, index)) + all_tasks[(job, index)] = task_type( + start=start_var, end=end_var, interval=interval_var) # Creates sequence variables and add disjunctive constraints. - machines_jobs = {} - for i in all_machines: - - machines_jobs[i] = [] - for j in all_jobs: - for k in range(0, len(machines[j])): - if machines[j][k] == i: - machines_jobs[i].append(all_tasks[j, k]) - model.AddNoOverlap(machines_jobs[i]) + for machine in all_machines: + intervals = [] + for job in all_jobs: + for index in range(0, len(machines[job])): + if machines[job][index] == machine: + intervals.append(all_tasks[(job, index)].interval) + model.AddNoOverlap(intervals) # Add precedence contraints. - for i in all_jobs: - for j in range(0, len(machines[i]) - 1): - model.Add(all_starts[i, j + 1] >= all_ends[i, j]) + for job in all_jobs: + for index in range(0, len(machines[job]) - 1): + model.Add( + all_tasks[(job, index + 1)].start >= all_tasks[(job, index)].end) - # Set the objective. - makespan = model.NewIntVar(0, horizon, 'makespan') - model.AddMaxEquality(makespan, - [all_ends[i, len(machines[i]) - 1] for i in all_jobs]) - model.Minimize(makespan) + # Makespan objective. + obj_var = model.NewIntVar(0, horizon, 'makespan') + model.AddMaxEquality( + obj_var, + [all_tasks[(job, len(machines[job]) - 1)].end for job in all_jobs]) + model.Minimize(obj_var) # Solve model. solver = cp_model.CpSolver() status = solver.Solve(model) - # Output solution. - disp_col_width = 10 if status == cp_model.OPTIMAL: - print("\nOptimal Schedule Length:", solver.ObjectiveValue(), "\n") - sol_line = "" - sol_line_tasks = "" - print("Optimal Schedule", "\n") + # Print out makespan. + print('Optimal Schedule Length: %i' % solver.ObjectiveValue()) + print() - for i in all_machines: - jobs = - sol_line += "Machine " + str(i) + ": " - sol_line_tasks += "Machine " + str(i) + ": " - sequence = collector.ForwardSequence(0, seq) - seq_size = len(sequence) + # Create one list of assigned tasks per machine. + assigned_jobs = [[] for _ in range(machines_count)] + for job in all_jobs: + for index in range(len(machines[job])): + machine = machines[job][index] + assigned_jobs[machine].append( + assigned_task_type( + start=solver.Value(all_tasks[(job, index)].start), + job=job, + index=index)) - for j in range(0, seq_size): - t = seq.Interval(sequence[j]); - # Add spaces to output to align columns. - sol_line_tasks += t.Name() + " " * (disp_col_width - len(t.Name())) + disp_col_width = 10 + sol_line = '' + sol_line_tasks = '' - for j in range(0, seq_size): - t = seq.Interval(sequence[j]); - sol_tmp = "[" + str(collector.Value(0, t.StartExpr().Var())) + "," - sol_tmp += str(collector.Value(0, t.EndExpr().Var())) + "] " + print('Optimal Schedule', '\n') + + for machine in all_machines: + # Sort by starting time. + assigned_jobs[machine].sort() + sol_line += 'Machine ' + str(machine) + ': ' + sol_line_tasks += 'Machine ' + str(machine) + ': ' + + for assigned_task in assigned_jobs[machine]: + name = 'job_%i_%i' % (assigned_task.job, assigned_task.index) # Add spaces to output to align columns. - sol_line += sol_tmp + " " * (disp_col_width - len(sol_tmp)) + sol_line_tasks += name + ' ' * (disp_col_width - len(name)) + start = assigned_task.start + duration = processing_times[assigned_task.job][assigned_task.index] - sol_line += "\n" - sol_line_tasks += "\n" + sol_tmp = '[%i,%i]' % (start, start + duration) + # Add spaces to output to align columns. + sol_line += sol_tmp + ' ' * (disp_col_width - len(sol_tmp)) + + sol_line += '\n' + sol_line_tasks += '\n' print(sol_line_tasks) - print("Time Intervals for Tasks\n") + print('Time Intervals for task_types\n') print(sol_line)