Esempio n. 1
0
    def optimize(
            self, input_params,
            timing_object: TimingData) -> Tuple[Testcase, EOptimizationStatus]:
        solver = CpSolver()
        solver.parameters.max_time_in_seconds = input_params.timeouts.timeout_scheduling
        print_model_stats(self.model.ModelStats())

        t = Timer()
        try:
            with t:
                for l_or_n_id in itertools.chain(self.tc.L.keys(),
                                                 self.tc.N.keys()):
                    self.model.AddDecisionStrategy(
                        list(self.o_f[l_or_n_id].values()),
                        cp_model.CHOOSE_LOWEST_MIN,
                        cp_model.SELECT_MIN_VALUE,
                    )

                self.model.AddDecisionStrategy(
                    list(self.o_t.values()),
                    cp_model.CHOOSE_LOWEST_MIN,
                    cp_model.SELECT_MIN_VALUE,
                )
                self.model.AddDecisionStrategy(
                    list(self.phi_f.values()),
                    cp_model.CHOOSE_LOWEST_MIN,
                    cp_model.SELECT_MIN_VALUE,
                )
                solver_status = solver.Solve(self.model)
                # print("Solved!\n{}".format(solver.ResponseStats()))
        except Exception as e:
            report_exception(e)
            solver_status = -1
        timing_object.time_optimizing_scheduling = t.elapsed_time

        status = EOptimizationStatus.INFEASIBLE

        if solver_status == OPTIMAL:
            status = EOptimizationStatus.OPTIMAL
        elif solver_status == FEASIBLE:
            status = EOptimizationStatus.FEASIBLE
        elif solver_status == INFEASIBLE:
            status = EOptimizationStatus.INFEASIBLE
        elif solver_status == UNKNOWN:
            status = EOptimizationStatus.UNKNOW
        elif solver_status == MODEL_INVALID:
            status = EOptimizationStatus.MODEL_INVALID

        if (status == EOptimizationStatus.FEASIBLE
                or status == EOptimizationStatus.OPTIMAL):
            schdl = schedule.from_cp_solver(solver, self, self.tc)
            self.tc.add_to_datastructures(schdl)
            return self.tc, status
        else:
            report_exception(
                "CPSolver returned invalid status for scheduling model: " +
                str(status))
            return self.tc, status
Esempio n. 2
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()
Esempio n. 3
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)
Esempio n. 4
0
    def optimize(
            self, input_params: InputParameters,
            timing_object: TimingData) -> Tuple[Testcase, EOptimizationStatus]:
        solver = CpSolver()
        solver.parameters.max_time_in_seconds = input_params.timeouts.timeout_pint
        print_model_stats(self.model.ModelStats())

        t = Timer()
        with t:
            solver_status = solver.Solve(self.model)
        timing_object.time_optimizing_pint = t.elapsed_time

        Pint = -1
        status = EOptimizationStatus.INFEASIBLE

        if solver_status == OPTIMAL:
            status = EOptimizationStatus.OPTIMAL
        elif solver_status == FEASIBLE:
            status = EOptimizationStatus.FEASIBLE
        elif solver_status == INFEASIBLE:
            status = EOptimizationStatus.INFEASIBLE
        elif solver_status == UNKNOWN:
            status = EOptimizationStatus.UNKNOW
        elif solver_status == MODEL_INVALID:
            status = EOptimizationStatus.MODEL_INVALID

        if (status == EOptimizationStatus.FEASIBLE
                or status == EOptimizationStatus.OPTIMAL):
            r = solver.Value(self.Pint_var)
            Pint = r

        else:
            raise ValueError(
                "CPSolver returned invalid status for Pint model: " +
                str(status))
        self.tc.Pint = Pint
        return self.tc, status
Esempio n. 5
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))
Esempio n. 6
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)
Esempio n. 7
0
    def optimize(
            self, input_params: InputParameters,
            timing_object: TimingData) -> Tuple[Testcase, EOptimizationStatus]:
        # Solve model_Pint.
        solver = CpSolver()
        solver.parameters.max_time_in_seconds = input_params.timeouts.timeout_routing
        print_model_stats(self.model.ModelStats())
        t = Timer()
        with t:
            for f_int in range(self.max_stream_int):
                self.model.AddDecisionStrategy(
                    list(self.x[f_int]),
                    cp_model.CHOOSE_LOWEST_MIN,
                    cp_model.SELECT_MIN_VALUE,
                )
                self.model.AddDecisionStrategy(
                    list(self.x_v_has_successor[f_int]),
                    cp_model.CHOOSE_LOWEST_MIN,
                    cp_model.SELECT_MIN_VALUE,
                )
                self.model.AddDecisionStrategy(
                    list(self.y[f_int]),
                    cp_model.CHOOSE_LOWEST_MIN,
                    cp_model.SELECT_MIN_VALUE,
                )

            solver_status = solver.Solve(self.model)
            # print(solver.ResponseStats())
        timing_object.time_optimizing_routing = t.elapsed_time

        status = EOptimizationStatus.INFEASIBLE

        if solver_status == OPTIMAL:
            status = EOptimizationStatus.OPTIMAL
        elif solver_status == FEASIBLE:
            status = EOptimizationStatus.FEASIBLE
        elif solver_status == INFEASIBLE:
            status = EOptimizationStatus.INFEASIBLE
        elif solver_status == UNKNOWN:
            status = EOptimizationStatus.UNKNOW
        elif solver_status == MODEL_INVALID:
            status = EOptimizationStatus.MODEL_INVALID

        # Output
        if (status == EOptimizationStatus.FEASIBLE
                or status == EOptimizationStatus.OPTIMAL):
            x_res, costs, route_lens, overlap_amounts, overlap_links = routing_model_results.generate_result_structures(
                self, solver)

            for f in self.tc.F_routed.values():
                mt = route(f)
                mt.init_from_x_res_vector(x_res[f.id])
                self.tc.add_to_datastructures(mt)

                r_info = route_info(mt, costs[f.id], route_lens[f.id],
                                    overlap_amounts[f.id], overlap_links[f.id])
                self.tc.add_to_datastructures(r_info)
        else:
            raise ValueError(
                "CPSolver in RoutingModel returned invalid status for routing model:"
                + str(status))

        return self.tc, status
def compute_optimal_bench_map(
    schedule: Schedule,
    all_employees: Dict[int, Employee],
    all_shifts: Dict[int, Shift],
    all_labs: Dict[str, Lab],
    all_benches: Dict[str, Bench],
    constraints: Constraints,
) -> Schedule:
    # Make sure employees have home_bench information
    for employee in all_employees:
        assert all_employees[employee].home_bench is not None

    model = CpModel()

    bench_placement = {}
    for shift in schedule.shift_schedule:
        for employee in schedule.shift_schedule[shift]:
            for bench in all_benches:
                bench_placement[(
                    shift, employee.index, bench)] = model.NewBoolVar(
                        f"bench_s{shift}e{employee.index}b{bench}")

    # 2 employees cannot have same bench
    for shift in schedule.shift_schedule:
        for bench in all_benches:
            model.Add(
                sum(bench_placement[(shift, employee.index, bench)]
                    for employee in schedule.shift_schedule[shift]) <= 1)

    # All employees scheduled must have a bench for each shift
    # Other employees must not have a bench
    for shift in schedule.shift_schedule:
        for employee in schedule.shift_schedule[shift]:
            model.Add(
                sum(bench_placement[(shift, employee.index, bench)]
                    for bench in all_benches) == 1)

    # Make sure appropriate benches are utilized during appropriate shift
    for shift in schedule.shift_schedule:
        for bench in all_benches:
            if (all_shifts[shift].label
                    == "FULL") and (all_benches[bench].active_shift == "AM"):
                # This works because we want FULL shifts to use AM benches
                # If proper condition, no constraint will be added
                # If anything else, it will be added
                # For example, FULL + PM will have constraint == 0
                pass  # important
            elif (all_shifts[shift].label != all_benches[bench].active_shift):
                model.Add(
                    sum(bench_placement[(shift, employee.index, bench)]
                        for employee in schedule.shift_schedule[shift]) == 0)

    # Create objective
    # Minimize distance from home bench to placed bench
    model.Minimize(
        sum(((
            employee.home_bench.location.x  # type: ignore
            - all_benches[bench].location.x)**2 + (
                employee.home_bench.location.y  # type: ignore
                - all_benches[bench].location.y)**2 + (
                    constraints.raw_lab_separation.loc[  # type: ignore
                        employee.home_bench.lab.name,  # type: ignore
                        all_benches[bench].lab.name, ])) *
            bench_placement[(shift, employee.index, bench)]
            for shift in schedule.shift_schedule
            for employee in schedule.shift_schedule[shift]
            for bench in all_benches))

    st.write("Running Location Optimization...")
    solver = CpSolver()
    solver.parameters.max_time_in_seconds = 60
    solver.Solve(model)
    st.write("Finished Location Optimization!")

    shift_bench_schedule: Dict[int, Dict[int, Bench]] = {
    }  # {Shift.id: {Employee.id: Bench}}
    shift_bench_schedule = {
        shift: {
            employee.index: all_benches[bench]
            for employee in schedule.shift_schedule[shift]
            for bench in all_benches
            if solver.Value(bench_placement[(shift, employee.index, bench)])
        }
        for shift in all_shifts
    }
    employee_bench_schedule: Dict[int, Dict[int, Bench]] = {
    }  # {Employee.id: {Shift.id: Bench}}
    for shift in shift_bench_schedule:
        for employee in shift_bench_schedule[shift]:
            employee_bench_schedule[employee] = {}
    for shift in shift_bench_schedule:
        for employee in shift_bench_schedule[shift]:
            employee_bench_schedule[employee][shift] = shift_bench_schedule[
                shift][employee]

    employee_bench_distances: Dict[int, float] = {
    }  # {Employee.id: average distance}
    for employee in employee_bench_schedule:
        employee_bench_distances[employee] = statistics.mean([
            ((employee_bench_schedule[employee][shift].location.x -
              all_employees[employee].home_bench.location.x)**2 +
             (employee_bench_schedule[employee][shift].location.y -
              all_employees[employee].home_bench.location.y)**2)**0.5
            for shift in employee_bench_schedule[employee]
        ])

    schedule.shift_bench_schedule = shift_bench_schedule
    schedule.employee_bench_schedule = employee_bench_schedule
    schedule.bench_objective_score = solver.ObjectiveValue()
    schedule.bench_optimization_average_distance = employee_bench_distances
    return schedule
def compute_optimal_schedule(
    all_employees: Dict[int, Employee],
    all_shifts: Dict[int, Shift],
    all_days: Dict[str, Day],
    schedule_parameters: ConstraintParameters,
) -> Schedule:
    model = CpModel()

    shifts = {}
    for employee in all_employees:
        for shift in all_shifts:
            shifts[(employee,
                    shift)] = model.NewBoolVar(f"shift_e{employee}s{shift}")

    # Each shift has max number of people
    # Up to input to calculate AM + FULL and PM + FULL true constraints
    for shift in all_shifts:
        model.Add(
            sum(shifts[(employee, shift)] for employee in all_employees) <=
            all_shifts[shift].max_capacity)

    # Each person has max slots
    for employee in all_employees:
        model.Add(
            sum(shifts[(employee,
                        shift)] if all_shifts[shift].label != "FULL" else 2 *
                shifts[(employee, shift)]
                for shift in all_shifts) <= all_employees[employee].max_slots)

    # A person can only have one of AM, PM, or FULL per day
    for employee in all_employees:
        for day in all_days:
            model.Add(
                sum(shifts[(employee, shift)] for shift in all_shifts
                    if all_shifts[shift].day == day) <= 1)

    # A person might not be able to work all possible shifts
    for employee in all_employees:
        for shift in all_shifts:
            if all_employees[employee].preferences[shift] <= -100:
                model.Add(sum([shifts[(employee, shift)]]) < 1)

    # Workaround for minimizing variance of scores
    # Instead, minimize difference between max score and min score
    # From: https://stackoverflow.com/a/53363585
    employee_scores = {}
    for employee in all_employees:
        employee_scores[employee] = model.NewIntVar(
            -100, 100, f"employee_score_{employee}")
        model.Add(employee_scores[employee] == sum(
            all_employees[employee].preferences[shift] *
            shifts[(employee,
                    shift)] if all_shifts[shift].label != "FULL" else 2 *
            all_employees[employee].preferences[shift] *
            shifts[(employee, shift)] for shift in all_shifts))
    min_employee_score = model.NewIntVar(-100, 100, "min_employee_score")
    max_employee_score = model.NewIntVar(-100, 100, "max_employee_score")
    model.AddMinEquality(min_employee_score,
                         [employee_scores[e] for e in all_employees])
    model.AddMaxEquality(max_employee_score,
                         [employee_scores[e] for e in all_employees])
    # Max Unfairness constraint
    if schedule_parameters.max_unfairness >= 0:
        model.Add(max_employee_score -
                  min_employee_score <= schedule_parameters.max_unfairness)

    # Create objective
    # Maximize points from requests
    # Maximize number of shifts filled
    # Minimize variance of scores per employee
    weights: Dict[str, int] = {
        "preferences":
        int(10 * (1 / (1 + schedule_parameters.fill_schedule_weight +
                       schedule_parameters.fairness_weight))),
        "fill_schedule":
        int(10 * (schedule_parameters.fill_schedule_weight /
                  (1 + schedule_parameters.fill_schedule_weight +
                   schedule_parameters.fairness_weight))),
        "fairness":
        int(10 * (schedule_parameters.fairness_weight /
                  (1 + schedule_parameters.fill_schedule_weight +
                   schedule_parameters.fairness_weight))),
    }
    model.Maximize(weights["preferences"] * sum(
        all_employees[employee].preferences[shift] * shifts[
            (employee, shift)] if all_shifts[shift].label != "FULL" else 2 *
        all_employees[employee].preferences[shift] * shifts[(employee, shift)]
        for employee in all_employees for shift in all_shifts) +
                   weights["fill_schedule"] * sum(shifts[(employee, shift)]
                                                  for employee in all_employees
                                                  for shift in all_shifts) -
                   weights["fairness"] *
                   (max_employee_score - min_employee_score))

    st.write("Constraints set up. Running Optimization...")
    solver = CpSolver()
    solver.parameters.max_time_in_seconds = 60
    solver.Solve(model)
    st.write("Finished Schedule Optimization!")

    # Prepare results
    shifts_per_employee: Dict[int, int] = {
        employee: sum(
            solver.Value(shifts[(
                employee,
                shift)]) if all_shifts[shift].label != "FULL" else 2 *
            solver.Value(shifts[(employee, shift)]) for shift in all_shifts)
        for employee in all_employees
    }
    score_per_employee: Dict[int, int] = {
        employee:
        sum(all_employees[employee].preferences[shift] * solver.Value(shifts[
            (employee, shift)]) if all_shifts[shift].label != "FULL" else 2 *
            all_employees[employee].preferences[shift] *
            solver.Value(shifts[(employee, shift)]) for shift in all_shifts)
        for employee in all_employees
    }
    score_variance: float = statistics.variance(score_per_employee.values())
    total_possible_shifts: int = sum(
        all_shifts[shift].
        max_capacity if all_shifts[shift].label != "FULL" else 2 *
        all_shifts[shift].max_capacity for shift in all_shifts)
    employee_schedule: Dict[int, List[Shift]] = {
        employee: [
            all_shifts[shift] for shift in all_shifts
            if solver.Value(shifts[(employee, shift)])
        ]
        for employee in all_employees
    }
    shift_schedule: Dict[int, List[Employee]] = {
        shift: [
            all_employees[employee] for employee in all_employees
            if solver.Value(shifts[(employee, shift)])
        ]
        for shift in all_shifts
    }

    schedule: Schedule = Schedule(
        objective_score=solver.ObjectiveValue(),
        time_to_solve=solver.WallTime(),
        number_of_shifts_per_employee=shifts_per_employee,
        score_per_employee=score_per_employee,
        score_variance=score_variance,
        min_employee_score=solver.Value(min_employee_score),
        max_employee_score=solver.Value(max_employee_score),
        total_possible_shifts=total_possible_shifts,
        total_shifts_filled=sum(shifts_per_employee.values()),
        employee_schedule=employee_schedule,
        shift_schedule=shift_schedule,
    )
    return schedule