def _init_constraints( model: cp_model.CpModel, loop: typing.Sequence[int], n_items: int, lower_bound: int, upper_bound: int, min_sum: int, max_sum: int, variables: typing.Tuple[cp_model.IntVar, ...], ): combinations = set([]) cycled_items = itertools.cycle(variables) for _ in range(n_items): for loopsize in loop: combination = tuple(next(cycled_items) for __ in range(loopsize)) combinations.add(combination) for nth_combination, combination in enumerate(combinations): sum_variable = model.NewIntVar( lower_bound * len(combination), upper_bound * len(combination), "SumVar{}".format(nth_combination), ) model.Add(sum_variable == sum(combination)) model.AddModuloEquality(0, sum_variable, 4) model.Add(sum_variable >= min_sum) model.Add(sum_variable <= max_sum)
def check_hint(model: cp_model.CpModel) -> bool: """Check the hinted solution is valid""" # No hint if not model.Proto().solution_hint.vars: return True # Clone model tmp_model = cp_model.CpModel() tmp_model.Proto().CopyFrom(model.Proto()) solver = cp_model.CpSolver() variables = tmp_model.Proto().solution_hint.vars values = tmp_model.Proto().solution_hint.values for i, var in enumerate(variables): var = tmp_model.Proto().variables[var] value = values[i] lb, ub = var.domain if not (lb <= value <= ub): # outside the domain return False var.ClearField("domain") var.domain.extend([value, value]) # hopefully presolved status = solver.Solve(tmp_model) return status in [cp_model.FEASIBLE, cp_model.OPTIMAL]
def _add_variables(self, graph: Graph, model: cp_model.CpModel): ub = graph.vert_amount vertex_colors = { i: model.NewIntVar(0, ub, "color"+str(i)) for i in range(graph.vert_amount) } max_color = model.NewIntVar(0, ub, "max_color") return vertex_colors, max_color
def __init__( self, tc: Testcase, timing_object: TimingData, input_params: InputParameters, do_simple_scheduling: bool = False, ): self.tc: testcase = tc self.Pint = tc.Pint self.mtrees: Dict[str, route] = tc.R # stream.id -> Route # Create the CP model self.model = CpModel() # Create variables t = Timer() with t: self._create_variables() scheduling_model_variables.init_variables(self, input_params) timing_object.time_creating_vars_scheduling = t.elapsed_time # Add constraints to CP model t = Timer() with t: if do_simple_scheduling: scheduling_model_constraints.add_simple_constraints(self) else: scheduling_model_constraints.add_constraints(self) # Add optimization goal scheduling_model_goals.maximize_laxity(self, do_simple_scheduling) timing_object.time_creating_constraints_scheduling = t.elapsed_time
def add_constraints(model: CpModel, letters: List) -> None: model.AddAllDifferent(letters) [s, e, n, d, m, o, r, y] = letters model.Add(s * K_BASE * K_BASE * K_BASE + e * K_BASE * K_BASE + n * K_BASE + d + m * K_BASE * K_BASE * K_BASE + o * K_BASE * K_BASE + r * K_BASE + e == m * K_BASE * K_BASE * K_BASE * K_BASE + o * K_BASE * K_BASE * K_BASE + n * K_BASE * K_BASE + e * K_BASE + y)
def solve_intermediate_objective( model: cp_model.CpModel, solver: cp_model.CpSolver, objective, hint=True, objective_type='min', callback: Optional[cp_model.CpSolverSolutionCallback] = None, alias=None, max_time=None, logger=logging): if max_time: solver.parameters.max_time_in_seconds = max_time if objective_type.lower().startswith('min'): model.Minimize(objective) objective_type = 'min' elif objective_type.lower().startswith('max'): model.Maximize(objective) objective_type = 'max' else: raise Exception(f'Can not "{objective_type}" objective') t0 = dt.datetime.now() if callback: status = solver.SolveWithSolutionCallback(model, callback) else: status = solver.Solve(model) duration = (dt.datetime.now() - t0).total_seconds() if status == cp_model.INFEASIBLE: logger.warning(f'INFEASIBLE solving {alias or objective}') return None, status elif status == cp_model.UNKNOWN: logger.warning(f'Time limit reached {alias or objective}') return None, status result = round(solver.ObjectiveValue()) if hint: hint_solution(model, solver) if not isinstance(objective, int): if status == cp_model.OPTIMAL: logger.debug( f'{alias or objective} == {result}, Seconds: {duration:.2f}') model.Add(objective == result) elif objective_type == 'min': logger.debug( f'{alias or objective} <= {result}, Seconds: {duration:.2f}') model.Add(objective <= result) elif objective_type == 'max': logger.debug( f'{alias or objective} >= {result}, Seconds: {duration:.2f}') model.Add(objective >= result) return result, status
def enforce_anti_affinity(model: cp_model.CpModel, alloc, task_anti_affinity, all_servers, all_resources): for (t1, t2) in task_anti_affinity: for s in all_servers: for r in all_resources: t2_assigned = model.NewBoolVar("t2_assigned_%s" % r) model.Add(alloc[t2, s, r] > 0).OnlyEnforceIf(t2_assigned) model.Add(alloc[t2, s, r] == 0).OnlyEnforceIf(t2_assigned.Not()) model.Add(alloc[t1, s, r] == 0).OnlyEnforceIf(t2_assigned)
def enforce_resources_correlated(model: cp_model.CpModel, task, server, r1, r2, alloc, tasks): b_same = model.NewBoolVar('b_same_%s_%s' % (r1, r2)) model.Add(alloc[(task, server, r1)] == tasks[r1][task]).OnlyEnforceIf(b_same) model.Add(alloc[(task, server, r1)] != tasks[r1][task]).OnlyEnforceIf( b_same.Not()) model.Add(alloc[(task, server, r2)] == tasks[r2][task]).OnlyEnforceIf(b_same) model.Add(alloc[(task, server, r2)] == 0).OnlyEnforceIf(b_same.Not())
def enforce_resource_allocation(model: cp_model.CpModel, resource, task, server, alloc, tasks): idx = (task, server, resource) b_resource = model.NewBoolVar('b_resource_%s_%s_%s' % idx) # Implement b_resource == (alloc[(t, s, r)] == tasks[r][t]) model.Add(alloc[idx] != tasks[resource][task]).OnlyEnforceIf( b_resource.Not()) model.Add(alloc[idx] == tasks[resource][task]).OnlyEnforceIf(b_resource) # Resource is either 0 or tasks[resource][task] model.Add(alloc[idx] == 0).OnlyEnforceIf(b_resource.Not())
def __init__(self, tc: Testcase, timing_object: TimingData): self.tc = tc # Create the model_old self.model = CpModel() # Create variables t = Timer() with t: self._create_variables() timing_object.time_creating_vars_pint = t.elapsed_time
def __init__(self): self.model = CpModel() self.solver = CpSolver() # Ideal number of workers is 8, see https://or.stackexchange.com/a/4126 self.solver.parameters.num_search_workers = 8 self._known_names = set() self._vars_bool = {} self._vars_int = {} self._vars_weighted = [] self._vars_weighted_cost = [] self.printer = ObjectiveSolutionPrinter()
def addCountLessonsAtNthHour(model: CpModel, orm, timeslotNumber): """ This function creates an IntVar that will contain the number of lessons that take place at a certain hour-number. Hour-number means a number of a timeslot of a day. Will count all lessons that occupy one of the timeslots with the given number. Args: model(CpModel): The or-tools model for constraint programming optimization. orm(ORM): The object-relation-mapper script that contains lists with all data of the timetable. timeslotNumber(int): The number of the timeslot on each day to count the lessons at. Has to be >= 1 and <= TIMESLOTS_PER_DAY. Where TIMESLOTS_PER_DAY is the number of timeslots in the timetable that take place per day. Returns: nthHourCount(IntVar): The OR-Tools variable, containing the count of lessons at the given timeslotNumber. """ # Count created variables and constraints added to the CpModel for debugging and testing. variableCount = 0 linConstraintCount = 0 # Create variable for counting the number of lessons. nthHourCount = model.NewIntVar(0, len(orm.getLessons()), "nthHourCount") variableCount += 1 # Collect all BoolVars of all lessons, that indicate if the lesson # take place at a certain timeslot. boolVars = [] for lesson in orm.getLessons(): # Find the timeslots with the given timeslot number. for timeslot in [ t for t in orm.getTimeslots() if t.number == timeslotNumber ]: # Collect the timeslotBoolVar for the given timeslot. boolVars.append(lesson.timeslotBoolVars[ timeslot.id - 1]) # Timeslot IDs start at 1 but list index with 0. # Assign the sum of the collected BoolVars to the target variable. model.Add(nthHourCount == LinearExpr.Sum(boolVars)) linConstraintCount += 1 logger.logVariables("CountLessonsAt" + str(timeslotNumber) + "Hour", variableCount, linConstraintCount, 0) return nthHourCount
def _init_variables( model: cp_model.CpModel, n_items: int, lower_bound: int, upper_bound: int ) -> typing.Tuple[cp_model.IntVar, ...]: return tuple( model.NewIntVar(lower_bound, upper_bound, "var{}".format(nth_variable),) for nth_variable in range(n_items) )
def __init__(self, n=20, d=4, matrix=None, limit=40): # getting csp self.n = n self.d = d self.matrix = matrix # initializing the model self.model = CpModel() # adding variables to the model self.variables = self.add_variables() # adding constraints self.add_disallawed_assignments(self.matrix) solver = cp_model.CpSolver() self.gatherer = solutionGatherer(self.variables, limit) self.status = solver.SearchForAllSolutions(self.model, self.gatherer)
def __init__(self, tc: Testcase, timing_object: TimingData): self.tc = tc # Create the model self.model = CpModel() # Create variables t = Timer() with t: self._create_variables() timing_object.time_creating_vars_pint = t.elapsed_time # Add constraints to model t = Timer() with t: self._add_constraints() self._add_optimization_goal_maximize() timing_object.time_creating_constraints_pint = t.elapsed_time
def __init__(self, num_queens: int, printer: bool): # Initialize model and solver self._cp_model = CpModel() self._cp_solver = CpSolver() self._num_queens = num_queens self._indices = range(num_queens) # Initialize the board self._board = self._initialize_board() # Add constraint for exactly 1 queen in each row, col self._constrain_rows_and_columns() # Add constraint for at most 1 queen in each diagonal self.constrain_diagonal(self.backwards_diagonal_func()) self.constrain_diagonal(self.forwards_diagonal_func()) # Add constraint for exactly N queens on board self._constrain_num_queens() # initialize solution printer self._solution_printer = NQueensPrinter(self._board, printer)
def addFreeDaySemesterGroupVariables(model: CpModel, orm): """ This function creates variables for each semester group that wishes a free day. Which means the variable free_day of the SemesterGroup object is not None. In that case, a variable lessonsOnFreeDay is added to the SemesterGroup object. This variable will indicate the number of lessons, the semester group is participating at, take place at the wished free day. This is not equivalent to the number of occupied timeslots for the group on that day. The value of the free_day variable - if not None - has to be one of the constants in file Timeslot.py, indicating a weekday. E.g. 'MO' for monday. Args: model(CpModel): The or-tools model for constraint programming optimization. orm(ORM): The object-relation-mapper script that contains lists with all data of the timetable. """ # Count created variables and constraints added to the CpModel for debugging and testing. variableCount = 0 linConstraintCount = 0 # Filter and iterate over semester groups with a wished free day. for group in [ sg for sg in orm.getSemesterGroups() if sg.free_day is not None ]: freeDayId = Timeslot.getWeekdayID( group.free_day) # Get the weekday number of the free day. group.lessonsOnFreeDay = model.NewIntVar(0, group.max_lessons_per_day, "") # Create the var. variableCount += 1 # Collect all weekdayBoolVars of all lessons of the semester group and the specific weekday. boolVarsOnFreeDay = [ l.weekdayBoolVars[freeDayId - 1] for l in group.getLessons() ] model.Add(group.lessonsOnFreeDay == sum(boolVarsOnFreeDay)) linConstraintCount += 1 logger.logVariables("CountLessonsOnFreeDay:", variableCount, linConstraintCount, 0)
def __init__(self, tc: Testcase, timing_object: TimingData): self.tc = tc self.possible_paths: Dict[str, Dict[str, List[List[str]]]] = { } # stream_id -> Dict(receiver_es_id -> List[path]) # Create the model_old self.model = CpModel() # Create variables t = Timer() with t: self._create_variables() timing_object.time_creating_vars_pint = t.elapsed_time
def deadline_penalty(end, deadline, model: cp_model.CpModel): slots = WORKING_HOURS * SUBDIVISIONS * 2 flag = model.NewBoolVar("more than 2 days") model.Add(deadline - end > slots).OnlyEnforceIf(flag) model.Add(deadline - end <= slots).OnlyEnforceIf(flag.Not()) penalty = model.NewIntVar(0, 160, "penalty") model.Add(slots - deadline + end == penalty).OnlyEnforceIf(flag.Not()) model.Add(deadline - end - slots == penalty).OnlyEnforceIf(flag) return penalty
def __init__(self, tc: Testcase, timing_object: TimingData): self.tc = tc # Create the model self.model = CpModel() # Create variables t = Timer() with t: self._create_helper_variables() self._create_optimization_variables() routing_model_variables.init_helper_variables(self) routing_model_variables.init_optimization_variables(self) timing_object.time_creating_vars_routing = t.elapsed_time # Add constraints to model t = Timer() with t: routing_model_constraints.add_constraints(self) # Add optimization goal routing_model_goals.add_optimization_goal(self) timing_object.time_creating_constraints_routing = t.elapsed_time
def addCountGapsVariables(model: CpModel, orm: ORM): """ This function creates variables for each SemesterGroup object that count the number of certain gaps in their timetable. A gap od size 1 is defined as timeslot at which no lesson with the semester group takes place, but at both adjacent timeslots take lesson with the semester group place. There are also gaps with size 2, 3 and 4 timeslots possible. This function depends on a timetable with 6 timeslots per day. The created variables for each gap size are appended to each SemesterGroup object. The type of this variables is IntVar. The names will be oneGapCount, twoGapCount, threeGapCount and fourGapCount. Args: model(CpModel): The or-tools model for constraint programming optimization. orm(ORM): The object-relation-mapper script that contains lists with all data of the timetable. """ # Count created variables and constraints added to the CpModel for debugging and testing. variableCount = 0 otherConstraintCount = 0 linConstraintCount = 0 # Add bool vars for every semester group and every timeslot. They will indicate if a lesson, # the semester group participates at, takes place at the respective timeslot. # The variables are stored as dictionaries at each SemesterGroup object. for sg in orm.getSemesterGroups(): sg.timeslotOccupiedMap = { } # Map with Timeslot as keys and the respective BoolVar as value. for timeslot in orm.getTimeslots(): # Collect boolVars for the current timeslot of all Lessons of the semester group. lessonTimeslotBoolVars = [] for lesson in sg.getLessons( ): # timeslots IDs start with 1, so subtract 1 for list index. lessonTimeslotBoolVars.append( lesson.timeslotBoolVars[timeslot.id - 1]) # Create var that indicates if the semester group # has a lesson at the respective timeslot. tOccupied = model.NewBoolVar("") variableCount += 1 # Add constraints to fulfill: tOccupied == OR(lessonTimeslotBoolVars) # tOccupied shall be True if one of the timeslotBoolVars in the created list is True. model.AddBoolOr(lessonTimeslotBoolVars).OnlyEnforceIf(tOccupied) model.AddBoolAnd([b.Not() for b in lessonTimeslotBoolVars ]).OnlyEnforceIf(tOccupied.Not()) otherConstraintCount += 2 sg.timeslotOccupiedMap[timeslot] = tOccupied # Create variables for all possible gaps at all possible timeslots a gap can appear. for sg in orm.getSemesterGroups(): oneGaps = [ ] # List with variables for all possible gaps of 1 timeslot. twoGaps = [ ] # List with variables for all possible gaps of 2 timeslots. threeGaps = [] # "" fourGaps = [] # "" for day in range( orm.WEEKDAYS): # Iterate from monday to friday as index 0 .. 4 # Create variables for all gaps of size 1 on the current day. for i in range(1, orm.TIMESLOTS_PER_DAY - 1): # Iterate from 2nd to 2nd last hour. # Var for gap at timeslot i on day. gap = model.NewBoolVar("") # Select the timeslot before the gap, at the gap and after it. timeslotBefore = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i - 1] timeslot = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i] timeslotAfter = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 1] # There has to be a lesson for the semester group before and after but not at the # timeslot of the gap. # gap => [x][ ][x] model.AddBoolAnd([ sg.timeslotOccupiedMap[timeslotBefore], sg.timeslotOccupiedMap[timeslot].Not(), sg.timeslotOccupiedMap[timeslotAfter] ]).OnlyEnforceIf(gap) # If no lesson before, a lesson at or no lesson after the timeslot of the gap, there # is no gap. model.AddBoolOr([ sg.timeslotOccupiedMap[timeslotBefore].Not(), sg.timeslotOccupiedMap[timeslot], sg.timeslotOccupiedMap[timeslotAfter].Not() ]).OnlyEnforceIf(gap.Not()) oneGaps.append(gap) # Add gapVar to List. variableCount += 1 otherConstraintCount += 2 # Create variables for all gaps of size 2 on the current day. for i in range( 1, orm.TIMESLOTS_PER_DAY - 2 ): # Iterate from 2nd to 3rd last hour. (Starts of gaps of size 2) gap = model.NewBoolVar("") # Select the timeslots before the gap, of the gap, at the gap and after it. timeslotBefore = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i - 1] timeslot1 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i] timeslot2 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 1] timeslotAfter = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 2] # gap => [x][ ][ ][x] model.AddBoolAnd([ sg.timeslotOccupiedMap[timeslotBefore], sg.timeslotOccupiedMap[timeslot1].Not(), sg.timeslotOccupiedMap[timeslot2].Not(), sg.timeslotOccupiedMap[timeslotAfter] ]).OnlyEnforceIf(gap) model.AddBoolOr([ sg.timeslotOccupiedMap[timeslotBefore].Not(), sg.timeslotOccupiedMap[timeslot1], sg.timeslotOccupiedMap[timeslot2], sg.timeslotOccupiedMap[timeslotAfter].Not() ]).OnlyEnforceIf(gap.Not()) twoGaps.append(gap) variableCount += 1 otherConstraintCount += 2 for i in range( 1, orm.TIMESLOTS_PER_DAY - 3 ): # Iterate from 2nd to 4th last hour. (Starts of gaps of size 3) gap = model.NewBoolVar("") timeslotBefore = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i - 1] timeslot1 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i] timeslot2 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 1] timeslot3 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 2] timeslotAfter = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 3] # gap => [x][ ][ ][ ][x] model.AddBoolAnd([ sg.timeslotOccupiedMap[timeslotBefore], sg.timeslotOccupiedMap[timeslot1].Not(), sg.timeslotOccupiedMap[timeslot2].Not(), sg.timeslotOccupiedMap[timeslot3].Not(), sg.timeslotOccupiedMap[timeslotAfter] ]).OnlyEnforceIf(gap) model.AddBoolOr([ sg.timeslotOccupiedMap[timeslotBefore].Not(), sg.timeslotOccupiedMap[timeslot1], sg.timeslotOccupiedMap[timeslot2], sg.timeslotOccupiedMap[timeslot3], sg.timeslotOccupiedMap[timeslotAfter].Not() ]).OnlyEnforceIf(gap.Not()) threeGaps.append(gap) variableCount += 1 otherConstraintCount += 2 for i in range( 1, orm.TIMESLOTS_PER_DAY - 4 ): # Iterate from 2nd to 5th last hour. (Starts of gaps of size 4) gap = model.NewBoolVar("") timeslotBefore = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i - 1] timeslot1 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i] timeslot2 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 1] timeslot3 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 2] timeslot4 = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 3] timeslotAfter = orm.getTimeslots()[day * orm.TIMESLOTS_PER_DAY + i + 4] # gap => [x][ ][ ][ ][ ][x] model.AddBoolAnd([ sg.timeslotOccupiedMap[timeslotBefore], sg.timeslotOccupiedMap[timeslot1].Not(), sg.timeslotOccupiedMap[timeslot2].Not(), sg.timeslotOccupiedMap[timeslot3].Not(), sg.timeslotOccupiedMap[timeslot4].Not(), sg.timeslotOccupiedMap[timeslotAfter] ]).OnlyEnforceIf(gap) model.AddBoolOr([ sg.timeslotOccupiedMap[timeslotBefore].Not(), sg.timeslotOccupiedMap[timeslot1], sg.timeslotOccupiedMap[timeslot2], sg.timeslotOccupiedMap[timeslot3], sg.timeslotOccupiedMap[timeslot4], sg.timeslotOccupiedMap[timeslotAfter].Not() ]).OnlyEnforceIf(gap.Not()) fourGaps.append(gap) variableCount += 1 otherConstraintCount += 2 # Create vars for counting the number of gaps. sg.oneGapCount = model.NewIntVar(0, len(oneGaps), "") sg.twoGapCount = model.NewIntVar(0, len(twoGaps), "") sg.threeGapCount = model.NewIntVar(0, len(threeGaps), "") sg.fourGapCount = model.NewIntVar(0, len(fourGaps), "") variableCount += 4 # Add constraints for assigning the gapCount vars. # Sum of boolVars of all gaps. model.Add(sg.oneGapCount == sum(oneGaps)) model.Add(sg.twoGapCount == sum(twoGaps)) model.Add(sg.threeGapCount == sum(threeGaps)) model.Add(sg.fourGapCount == sum(fourGaps)) linConstraintCount += 4 logger.logVariables("GapVariables", variableCount, linConstraintCount, otherConstraintCount)
class WiTiProblem: wt_model = CpModel() wt_solver = CpSolver() tasks = [] tasks_nb = 0 """ Metoda load_from_file służy do załadowania danych z pliku. """ def load_from_file(self, file_name: str): file = open(file_name, "r") self.tasks_nb = int(next(file)) for id in range(0, self.tasks_nb): task = Task() row = next(file).split() task.time = (int(row[0])) task.penalty = (int(row[1])) task.deadline = (int(row[2])) task.id = id self.tasks.append(task) ################################################################################### """ Metoda solve uruchamia solver. """ def solve(self): print("\nSolver włączony") self.wt_solver.parameters.max_time_in_seconds = 8 self.wt_solver.Solve(self.wt_model) print("Solver zakończony\n") ################################################################################### """ Metoda run jest podstawową metodą definiowania problemu. """ def run(self, file_name): self.load_from_file(file_name) time_sum = 0 for task_nbr in range(0, self.tasks_nb): time_sum = time_sum + self.tasks[task_nbr].time late_sum = 0 for task_nbr in range(self.tasks_nb): late_sum += self.tasks[task_nbr].penalty * self.tasks[ task_nbr].deadline objective_min = 0 objective_max = late_sum + 1 variable_max_value = 1 + time_sum variable_min_value = 0 starts = [] finishes = [] intervals = [] lates = [] objective = self.wt_model.NewIntVar(objective_min, objective_max, "WiTi") for task_nbr in range(self.tasks_nb): nbr = str(task_nbr) start = self.wt_model.NewIntVar(variable_min_value, variable_max_value, "start" + nbr) finish = self.wt_model.NewIntVar(variable_min_value, variable_max_value, "finish" + nbr) interval = self.wt_model.NewIntervalVar(start, self.tasks[task_nbr].time, finish, "interval" + nbr) late = self.wt_model.NewIntVar(objective_min, objective_max, "late" + nbr) starts.append(start) finishes.append(finish) intervals.append(interval) lates.append(late) self.wt_model.AddNoOverlap(intervals) for task_nbr in range(self.tasks_nb): self.wt_model.Add(lates[task_nbr] >= 0) self.wt_model.Add( lates[task_nbr] >= (finishes[task_nbr] - self.tasks[task_nbr].deadline) * self.tasks[task_nbr].penalty) max_t = sum(lates) self.wt_model.Add(objective >= max_t) self.wt_model.Minimize(objective) self.solve() output_tasks_order = [] for task_nbr in range(self.tasks_nb): output_tasks_order.append( (task_nbr, self.wt_solver.Value(starts[task_nbr]))) output_tasks_order.sort(key=lambda x: x[1]) output_tasks_order = [x[0] for x in output_tasks_order] print("Suma: " + str(int(self.wt_solver.ObjectiveValue()))) print("Kolejność zadań: " + str(output_tasks_order))
def _add_constraints(self, graph: Graph, model: cp_model.CpModel, colors, max_color): # Add Constraints for edge in graph.edges: model.AddAllDifferent([colors[i] for i in edge]) model.AddMaxEquality(max_color, [colors[i] for i in colors])
def model(): return CpModel()
class JSProblem: js_model = CpModel() js_solver = CpSolver() tasks_nb = 0 machines_nb = 0 operations_nb = 0 """ Metoda load_from_file służy do załadowania danych z pliku. """ def load_from_file(self, file_name: str): file = open(file_name, "r") self.tasks_nb, self.machines_nb, self.operations_nb = [ int(x) for x in next(file).split() ] jobshop_data = [] for i in range(0, self.tasks_nb): row = next(file).split() operation_in_task = int(row[0]) single_job_data = [] for i in range(1, operation_in_task * 2, 2): m = int(row[i]) p = int(row[i + 1]) single_job_data.append((m, p)) jobshop_data.append(single_job_data) file.close() return jobshop_data ################################################################################### """ Metoda solve uruchamia solver. """ def solve(self): print("\nSolver włączony") self.js_solver.parameters.max_time_in_seconds = 8 self.js_solver.Solve(self.js_model) print("Solver zakończony\n") ################################################################################### """ Metoda print_result zajmuje się wypisaniem wyników działania solvera. """ def print_result(self, jobshop_matrix, all_tasks): assigned_task_type = cl.namedtuple('assigned_task_type', 'start job index') assigned_jobs = cl.defaultdict(list) for job_id, job in enumerate(jobshop_matrix): for task_id, task in enumerate(job): machine = task[0] assigned_jobs[machine].append( assigned_task_type( start=self.js_solver.Value(all_tasks[job_id, task_id].start), job=job_id, index=task_id, )) print("Cmax wynosi: " + str(int(self.js_solver.ObjectiveValue()))) for machine in range(1, self.machines_nb + 1): assigned_jobs[machine].sort() line_to_print = "Maszyna " + str(machine) + ': ' for assigned_task in assigned_jobs[machine]: name = assigned_task.job * self.machines_nb + assigned_task.index + 1 line_to_print += str(name) + " " print(line_to_print) ################################################################################### """ Metoda run jest podstawową metodą definiowania problemu. """ def run(self, filename): jobshop_data = self.load_from_file(filename) task_type = cl.namedtuple('task_type', 'start end interval') all_tasks = {} machine_to_intervals = cl.defaultdict(list) worst_cmax = sum(task[1] for job in jobshop_data for task in job) for job_id, job in enumerate(jobshop_data): for task_id, task in enumerate(job): machine = task[0] duration = task[1] start = self.js_model.NewIntVar(0, worst_cmax, 'start') finish = self.js_model.NewIntVar(0, worst_cmax, 'finish') interval_var = self.js_model.NewIntervalVar( start, duration, finish, 'interval') all_tasks[job_id, task_id] = task_type(start=start, end=finish, interval=interval_var) machine_to_intervals[machine].append(interval_var) for machine in range(1, self.machines_nb + 1): self.js_model.AddNoOverlap(machine_to_intervals[machine]) for job_id, job in enumerate(jobshop_data): for task_id in range(len(job) - 1): self.js_model.Add( all_tasks[job_id, task_id + 1].start >= all_tasks[job_id, task_id].end) cmax = self.js_model.NewIntVar(0, worst_cmax, 'cmax') self.js_model.AddMaxEquality(cmax, [ all_tasks[job_id, len(job) - 1].end for job_id, job in enumerate(jobshop_data) ]) self.js_model.Minimize(cmax) self.solve() self.print_result(jobshop_data, all_tasks)
class SASchedulingSolver: def __init__(self, tc: Testcase, timing_object: TimingData): self.tc = tc # Create the model_old self.model = CpModel() # Create variables t = Timer() with t: self._create_variables() timing_object.time_creating_vars_pint = t.elapsed_time def _create_variables(self): self.Pint_var = self.model.NewIntVar(0, self.tc.hyperperiod, "Pint") def _task_finish_time(self, t: task, task_start_times: Dict[str, int]): return task_start_times[t.id] + t.exec_time def _stream_finish_time(self, t1: task, t2: task, t_start_times: Dict[str, int]): if t1.src_es_id == t2.src_es_id: c_signal = 0 else: app = self.tc.A[t1.app_id] stream = self.tc.F_routed[app.edges_from_nodes[t1.id][t2.id][0]] route = self.tc.R[stream.id] c_signal = 0 for l in route.get_links_along_route_towards( t2.src_es_id, self.tc.L_from_nodes): c_signal += l.transmission_length(stream.size) return self._task_finish_time(t1, t_start_times) + c_signal def _es_finish_time(self, es_id: str, t_start_times: Dict[str, int]): finish_time = 0 for t_id, start_time in t_start_times.items(): t = self.tc.T[t_id] if es_id == t.src_es_id: if self._task_finish_time(t, t_start_times) > finish_time: finish_time = self._task_finish_time(t, t_start_times) return finish_time def _data_ready_time(self, t: task, t_start_times: Dict[str, int]): """ Returns the data ready time for a given task_id """ app = self.tc.A[t.app_id] predecessor_finish_times = [ self._stream_finish_time(self.tc.T[t_i_id], t, t_start_times) for t_i_id in app.get_predecessors_ids(t.id) ] if len(predecessor_finish_times) == 0: tdr = 0 else: tdr = max(predecessor_finish_times) return tdr def _list_schedule(self, sorted_task_list: List[str]): t_start_times: Dict[str, int] = {} for task_id in sorted_task_list: # End Technique task = self.tc.T[task_id] t_start_times[task_id] = max( self._data_ready_time(task, t_start_times), self._es_finish_time(task.src_es_id, t_start_times)) return t_start_times def _solve(self): # create initial ordering for app in self.tc.A_app.values(): order = app.get_topological_order() t_start_times = self._list_schedule(order) print(t_start_times) return t_start_times def optimize( self, input_params: InputParameters, timing_object: TimingData) -> Tuple[Testcase, EOptimizationStatus]: status = EOptimizationStatus.FEASIBLE t = Timer() with t: # OPTIMIZE start_times = self._solve() timing_object.time_optimizing_pint = t.elapsed_time if (status == EOptimizationStatus.FEASIBLE): # UPDATE tc self.tc.schedule = schedule() else: raise ValueError( "CPSolver returned invalid status for SAScheduling model_old: " + str(status)) return self.tc, status
class CSPSolver(object): def __init__(self, n=20, d=4, matrix=None, limit=40): # getting csp self.n = n self.d = d self.matrix = matrix # initializing the model self.model = CpModel() # adding variables to the model self.variables = self.add_variables() # adding constraints self.add_disallawed_assignments(self.matrix) solver = cp_model.CpSolver() self.gatherer = solutionGatherer(self.variables, limit) self.status = solver.SearchForAllSolutions(self.model, self.gatherer) def add_variables(self): """ Add variables to the CpModel Returns: returns the variables added to the CpModel """ variables = [None] * self.n for i in range(self.n): variables[i] = self.model.NewIntVar(0, self.d - 1, 'x' + str(i)) return variables def add_disallawed_assignments(self, matrix): """ Add constraints to the CpModel Args: matrix: matrix with forbidden assignments """ d = self.d disallowed = np.nonzero(matrix) for ix in range(disallowed[0].shape[0]): scope = [ self.variables[disallowed[0][ix] // d], self.variables[disallowed[1][ix] // d] ] assignment = [(int(disallowed[0][ix] % d), int(disallowed[1][ix] % d))] self.model.AddForbiddenAssignments(scope, assignment) def has_solutions(self): """ Indicates if the model is feasible or not Returns: True if the model is FEASIBLE, False otherwise """ if self.status == cp_model.FEASIBLE: return False return True def solution_count(self): """ Indicates if the number of solutions found Returns: Return the number of satisfying assignments """ return self.gatherer.solution_count() def get_satisfying_assignments(self, n=None): """ Gives n satisfing assignments for the given matrix Args: n: number of assignments required Returns: n satisfying assignments, if n is not specified all assignments are returned Raises: Exception if the number of required assignments is greater than the number of satisfying assignments """ if (n != None) and (n > self.gatherer.solution_count()): raise Exception( 'The number of solution required is greater than the number of satisfying assingments' ) solutions = self.gatherer.get_solutions() if n != None: solutions = solutions[:n] return solutions
def hint_solution(model: cp_model.CpModel, solver: cp_model.CpSolver) -> None: """Hint all the variables of a model with its solution.""" model.Proto().solution_hint.Clear() variables = range(len(model.Proto().variables)) model.Proto().solution_hint.vars.extend(variables) model.Proto().solution_hint.values.extend(solver.ResponseProto().solution)
class _Solver: """Generic solver class.""" def __init__(self): self.model = CpModel() self.solver = CpSolver() # Ideal number of workers is 8, see https://or.stackexchange.com/a/4126 self.solver.parameters.num_search_workers = 8 self._known_names = set() self._vars_bool = {} self._vars_int = {} self._vars_weighted = [] self._vars_weighted_cost = [] self.printer = ObjectiveSolutionPrinter() def create_variable_bool(self, name: str = None) -> IntVar: """Create a boolean variable. :param name: The variable human readable name. :return: The new variable """ assert name not in self._vars_bool assert name not in self._known_names var = self.model.NewBoolVar(name) self._vars_bool[name] = var return var def create_variable_int( self, key: str, minimum: int, maximum: int, name: str = None ) -> IntVar: """Create an integer variable. :param key: An hashable value to use as an identifier. :param minimum: The variable domain minimum value. :param maximum: The variable domain maximum value. :param name: The variable human readable name. :return: The new variable """ name = name or "_".join(key) assert key not in self._vars_int assert name not in self._known_names var = self.model.NewIntVar(minimum, maximum, name) self._vars_int[key] = var return var def get_variable_bool(self, key: Hashable) -> IntVar: """Get an already defined boolean variable. :param key: A hashable key :return: A variable :raises KeyError: If no variable exist for the provided key. """ return self._vars_bool[key] def add_hard_constraint(self, expr: BoundedLinearExpression) -> Constraint: """Add a "hard" constraint. T he solve will fail if even once hard constraint fail to be satisfied. :param expr: The constraint expression :return: A constraint """ return self.model.Add(expr) def set_variable_bool_score(self, variable: Constraint, score: int) -> None: """Set the cost for a variable. :param variable: A variable :param score: The cost if the variant is ON """ self._vars_weighted.append(variable) self._vars_weighted_cost.append(score) def create_soft_constraint_bool(self, name: str, score: int) -> Constraint: """Add a "soft" boolean variable with a score. The solver will try to maximize it's score. :param str name: The variable name :param int score: The variable score :return: A constraint """ assert name not in self._known_names var = self.model.NewBoolVar(name) self.set_variable_bool_score(var, score) return var def create_soft_constraint_int( self, name: str, minimum: int, maximum: int, score: int ) -> IntVar: """Add a "soft" integer variable with a score. The solver will try to maximum it's score. :param name: The variable name :param minimum: The variable domain minimum value :param maximum: The variable domain maximum value :param score: The variable score :return: A constraint """ assert name not in self._known_names var = self.model.NewIntVar(minimum, maximum, name) self._vars_weighted.append(var) self._vars_weighted_cost.append(score) return var def iter_variables_and_cost(self) -> Generator[Tuple[IntVar, int], None, None]: """Yield all variables and their score multipliers.""" for variable, score in zip(self._vars_weighted, self._vars_weighted_cost): yield variable, score def solve(self): """Solve using provided constraints.""" self.model.Maximize( sum( var * cost for var, cost in zip( itertools.chain( self._vars_weighted, self._vars_weighted, ), itertools.chain( self._vars_weighted_cost, self._vars_weighted_cost, ), ) ) ) status = self.solver.SolveWithSolutionCallback(self.model, self.printer) if status not in (OPTIMAL, FEASIBLE): raise RuntimeError("No solution found! Status is %s" % status) return status
def addGapBetweenDaysTeacherVariables(model: CpModel, orm): """ This function creates variables for each possible gap of days. One day gap, two day gap and three day gap. These gaps are only of the view of timetables of teachers. And the variables are only created for teachers with enabled avoid_free_day_gaps flag. On these Teacher objects, variables oneDayGaps, twoDayGaps ans threeDayGaps of the type IntVar, are added in this function. Args: model(CpModel): The or-tools model for constraint programming optimization. orm(ORM): The object-relation-mapper script that contains lists with all data of the timetable. """ # Count created variables and constraints added to the CpModel for debugging and testing. variableCount = 0 linConstraintCount = 0 otherConstraintCount = 0 # Iterate over teachers with the avoid_free_day_gaps flag set. for teacher in filter( lambda t: t.avoid_free_day_gaps and len(t.lessons) > 1, orm.getTeachers()): # Create a list with BoolVars for the teacher and each weekday. # Each BoolVar will indicate if at least a lesson takes place on the respective day. workingDayBools = [model.NewBoolVar("") for x in range(orm.WEEKDAYS)] # Create a negated variant of each BoolVar since _NotBooleanVariable type is not allowed # for the MinEquality Constraint. (Maybe a bug in the Python implementation of OR-Tools) workingDayBoolsNeg = [ model.NewBoolVar("") for x in range(orm.WEEKDAYS) ] variableCount += 2 * len(workingDayBools) # Add assignments for each day and the respective variables. for i in range(orm.WEEKDAYS): # workingDayBool for the weekday of index i shall be True if at least of the teachers # lessons takes place at the respective weekday. # Make: workingDayBools[i] <==> lesson_1.weekdayBoolVars[i] OR lesson_2.weekdayBoolVars[i] OR ... OR lesson_n.weekdayBoolVars[i] model.AddMaxEquality( workingDayBools[i], [l.weekdayBoolVars[i] for l in teacher.lessons]) # Assign negative variants. model.Add(workingDayBoolsNeg[i] == workingDayBools[i].Not()) otherConstraintCount += 1 linConstraintCount += 1 # Create a BoolVar variable for each possible day gap. # Only on tuesday, wednesday or thursday, day gaps can occur. Or more general said, on # the second to the second last day of the week. # Day gaps of size 1: # Gap on tuesday, means lessons on monday and wednesday but not on tuesday. oneDayGaps = [] for dayIndex in range(1, orm.WEEKDAYS - 1): oneDayGap = model.NewBoolVar("One_Day_Gap") # Add constraints for assigning the gap variables. model.AddMinEquality(oneDayGap, [ workingDayBools[dayIndex - 1], workingDayBoolsNeg[dayIndex], workingDayBools[dayIndex + 1] ]) # E.g.: tuesdayGap <==> lessonOnMonday AND lessonOnTuesday.Not() AND lessonOnWednesday oneDayGaps.append(oneDayGap) variableCount += 1 otherConstraintCount += 1 # Day gaps of size 2: # Gap on tuesday and wednesday, means lessons on monday and thursday but not on tuesday # and wednesday. twoDayGaps = [] for dayIndex in range(1, orm.WEEKDAYS - 2): twoDayGap = model.NewBoolVar("Two_Day_Gap") # Add constraints for assigning the gap variables. model.AddMinEquality(twoDayGap, [ workingDayBools[dayIndex - 1], workingDayBoolsNeg[dayIndex], workingDayBoolsNeg[dayIndex + 1], workingDayBools[dayIndex + 2] ]) # E.g.: tue_wedGap <==> lessonOnMonday AND lessonOnTuesday.Not() AND lessonOnWednesday.Not() AND lessonOnThursday twoDayGaps.append(twoDayGap) variableCount += 1 otherConstraintCount += 1 # Day gaps of size 3: # Gap on tuesday, wednesday and thursday, means lessons on monday and friday but not on # tuesday, wednesday, thursday. threeDayGaps = [] for dayIndex in range(1, orm.WEEKDAYS - 3): threeDayGap = model.NewBoolVar("Three_Day_Gap") # Add constraints for assigning the gap variables. model.AddMinEquality(threeDayGap, [ workingDayBools[dayIndex - 1], workingDayBoolsNeg[dayIndex], workingDayBoolsNeg[dayIndex + 1], workingDayBoolsNeg[dayIndex + 2], workingDayBools[dayIndex + 3] ]) # E.g.: tue_wed_thuGap <==> lessonOnMonday AND eventOnTuesday.Not() AND lessonOnWednesday.Not() AND lessonOnThursday.Not() AND lessonOnFriday threeDayGaps.append(threeDayGap) variableCount += 1 otherConstraintCount += 1 # Create and assign the gapCount variables. teacher.oneDayGapCount = model.NewIntVar(0, len(oneDayGaps), "") # In fact, the maximum number of gaps of size 1 is much smaller than len(oneDayGaps) because # some gaps are mutually exclusive. E.g. there cannot be a one day gap on tuesday and # wednesday. But use this to large upper bound to avoid determining the actual maximum number. model.Add(teacher.oneDayGapCount == sum(oneDayGaps)) teacher.twoDayGapCount = model.NewIntVar(0, len(twoDayGaps), "") model.Add(teacher.twoDayGapCount == sum(twoDayGaps)) teacher.threeDayGapCount = model.NewIntVar(0, len(threeDayGaps), "") model.Add(teacher.threeDayGapCount == sum(threeDayGaps)) variableCount += 3 otherConstraintCount += 3 logger.logVariables("TeacherAvoidFreeDaysBetweenWorkingDays", variableCount, linConstraintCount, otherConstraintCount)