예제 #1
0
    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)
예제 #2
0
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]
예제 #3
0
 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
예제 #4
0
    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)
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
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())
예제 #9
0
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())
예제 #10
0
    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
예제 #11
0
 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()
예제 #12
0
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
예제 #13
0
 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)
     )
예제 #14
0
    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)
예제 #15
0
    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
예제 #16
0
    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)
예제 #17
0
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)
예제 #18
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
예제 #19
0
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
예제 #20
0
    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
예제 #21
0
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)
예제 #22
0
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))
예제 #23
0
 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])
예제 #24
0
def model():
    return CpModel()
예제 #25
0
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)
예제 #26
0
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
예제 #27
0
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
예제 #28
0
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)
예제 #29
0
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
예제 #30
0
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)