def solve(self): T = self.g.size with self.profiler("Gurobi model optimization", extra_data={"T": str(T), "budget": str(self.budget)}): with Timer("ILPSolve") as solve_ilp: self.m.optimize() self.solve_time = solve_ilp.elapsed infeasible = self.m.status == GRB.INFEASIBLE try: _ = self.R[0, 0].X _ = self.S[0, 0].X _ = self.U[0, 0].X _ = self.batch_size.X except AttributeError as e: infeasible = True if infeasible: raise ValueError("Infeasible model, check constraints carefully. Insufficient memory?") Rout = np.zeros((T, T), dtype=SOLVER_DTYPE) Sout = np.zeros((T, T), dtype=SOLVER_DTYPE) Uout = np.zeros((T, T), dtype=SOLVER_DTYPE) Free_Eout = np.zeros((T, len(self.g.edge_list)), dtype=SOLVER_DTYPE) batch_size = self.batch_size.X try: for t in range(T): for i in range(T): Rout[t][i] = int(self.R[t, i].X) Sout[t][i] = int(self.S[t, i].X) Uout[t][i] = self.U[t, i].X * self.ram_gcd for e in range(len(self.g.edge_list)): Free_Eout[t][e] = int(self.Free_E[t, e].X) except AttributeError as e: logging.exception(e) return None, None, None, None Rout = solve_r_opt(self.g, Sout) # prune R using optimal recomputation solver ilp_aux_data = ILPAuxData( U=Uout, Free_E=Free_Eout, ilp_approx=False, ilp_time_limit=0, ilp_eps_noise=0, ilp_num_constraints=self.m.numConstrs, ilp_num_variables=self.m.numVars, ) schedule, aux_data = schedule_from_rs(self.g, Rout, Sout) return ( ScheduledResult( solve_strategy=SolveStrategy.OPTIMAL_ILP_GC, solver_budget=self.budget, feasible=True, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=self.solve_time, ilp_aux_data=ilp_aux_data, ), batch_size, )
def solve_chen_greedy(g: DFGraph, segment_mem_B: int, use_actuation_points: bool = True): with Timer("solve_chen_greedy") as timer_solve: C = g.articulation_points if use_actuation_points else g.v temp = 0 x = 0 checkpoints = set() for v in g.topological_order_fwd: temp += g.cost_ram[v] if v in C and temp > segment_mem_B: x += g.cost_ram[v] temp = 0 checkpoints.add(v) S = gen_s_matrix_fixed_checkpoints(g, checkpoints) R = solve_r_opt(g, S) schedule, aux_data = schedule_from_rs(g, R, S) return ScheduledResult( solve_strategy=SolveStrategy.CHEN_GREEDY if use_actuation_points else SolveStrategy.CHEN_GREEDY_NOAP, solver_budget=segment_mem_B, feasible=True, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=timer_solve.elapsed, )
def solve_checkpoint_all(g: DFGraph): with Timer("solve_checkpoint_all") as timer_solve: s = gen_s_matrix_fixed_checkpoints(g, g.vfwd) r = solve_r_opt(g, s) schedule, aux_data = schedule_from_rs(g, r, s) return ScheduledResult( solve_strategy=SolveStrategy.CHECKPOINT_ALL, solver_budget=0, feasible=True, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=timer_solve.elapsed, )
def solve_checkpoint_last_node(g: DFGraph): """Checkpoint only one node between stages""" with Timer("solve_checkpoint_last_node") as timer_solve: s = np.zeros((g.size, g.size), dtype=SOLVER_DTYPE) np.fill_diagonal(s[1:], 1) r = solve_r_opt(g, s) schedule, aux_data = schedule_from_rs(g, r, s) return ScheduledResult( solve_strategy=SolveStrategy.CHECKPOINT_LAST_NODE, solver_budget=0, feasible=True, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=timer_solve.elapsed, )
def solve_chen_sqrtn(g: DFGraph, use_actuation_points: bool = True) -> ScheduledResult: with Timer("solve_chen_sqrtn") as timer_solve: C = g.articulation_points if use_actuation_points else g.v k = int(math.sqrt(len(C))) checkpoints = [v for idx, v in enumerate(C) if (idx + 1) % k == 0] S = gen_s_matrix_fixed_checkpoints(g, set(checkpoints)) R = solve_r_opt(g, S) schedule, aux_data = schedule_from_rs(g, R, S) return ScheduledResult( solve_strategy=SolveStrategy.CHEN_SQRTN if use_actuation_points else SolveStrategy.CHEN_SQRTN_NOAP, solver_budget=0, feasible=True, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=timer_solve.elapsed, )
def solve_checkmate_cvxpy( g, budget, rounding_thresholds=(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9), solver_override=None, verbose=True ): lpsolver = ILPSolverCVXPY(g, int(0.9 * budget)) # rounding threshold try: r, s, u, free_e = lpsolver.solve(solver_override=solver_override, verbose=verbose) lp_feasible = True except ValueError as e: logging.exception(e) r, s, u, free_e = (None, None, None, None) lp_feasible = False schedule, aux_data, min_threshold = None, None, None if lp_feasible: # round the solution for threshold in rounding_thresholds: s_ = (s >= threshold).astype(np.int) r_ = solve_r_opt(g, s_) schedule_, aux_data_ = schedule_from_rs(g, r_, s_) if aux_data_.activation_ram <= budget and (aux_data is None or aux_data_.cpu <= aux_data.cpu): aux_data = aux_data_ schedule = schedule_ min_threshold = threshold solve_strategy = ( SolveStrategy.APPROX_DET_ROUND_LP_05_THRESH if len(rounding_thresholds) == 1 else SolveStrategy.APPROX_DET_ROUND_LP_SWEEP ) return ScheduledResult( solve_strategy=solve_strategy, solver_budget=budget, feasible=lp_feasible and aux_data is not None, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=lpsolver.solve_time, ilp_aux_data=ILPAuxData( U=u, Free_E=free_e, ilp_approx=False, ilp_time_limit=None, ilp_eps_noise=0.0, ilp_num_constraints=lpsolver.num_vars, ilp_num_variables=lpsolver.num_constraints, approx_deterministic_round_threshold=min_threshold, ), )
def solve_approx_lp_deterministic_sweep( g: DFGraph, budget: int, seed_s: Optional[np.ndarray] = None, approx=True, time_limit: Optional[int] = None, write_log_file: Optional[PathLike] = None, print_to_console=True, write_model_file: Optional[PathLike] = None, eps_noise=0.01, solver_cores=os.cpu_count(), thresholds=(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9), imposed_schedule: ImposedSchedule = ImposedSchedule.FULL_SCHEDULE, allow_return_infeasible_schedule=False, ): param_dict = { "LogToConsole": 1 if print_to_console else 0, "LogFile": str(write_log_file) if write_log_file is not None else "", "Threads": solver_cores, "TimeLimit": math.inf if time_limit is None else time_limit, "OptimalityTol": 1e-2 if approx else 1e-4, "IntFeasTol": 1e-3 if approx else 1e-5, "Presolve": 2, "StartNodeLimit": 10000000, } lpsolver = ILPSolverGurobi( g, int(0.9 * budget), # hack to get values under the budget gurobi_params=param_dict, seed_s=seed_s, integral=False, solve_r=False, eps_noise=eps_noise, imposed_schedule=imposed_schedule, write_model_file=write_model_file, ) lpsolver.build_model() try: r, s, u, free_e = lpsolver.solve() lp_feasible = True except ValueError as e: logging.exception(e) r, s, u, free_e = (None, None, None, None) lp_feasible = False schedule, aux_data, min_threshold = None, None, None if lp_feasible: # round the solution for threshold in thresholds: s_ = (s >= threshold).astype(np.int) r_ = solve_r_opt(g, s_) schedule_, aux_data_ = schedule_from_rs(g, r_, s_) if (allow_return_infeasible_schedule and aux_data is None) or ( aux_data_.activation_ram <= budget and (aux_data is None or aux_data_.cpu <= aux_data.cpu)): aux_data = aux_data_ schedule = schedule_ min_threshold = threshold return ScheduledResult( solve_strategy=SolveStrategy.APPROX_DET_ROUND_LP_SWEEP, solver_budget=budget, feasible=lp_feasible and aux_data is not None, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=lpsolver.solve_time, ilp_aux_data=ILPAuxData( U=u, Free_E=free_e, ilp_approx=approx, ilp_time_limit=time_limit, ilp_eps_noise=eps_noise, ilp_num_constraints=lpsolver.m.numConstrs, ilp_num_variables=lpsolver.m.numVars, approx_deterministic_round_threshold=min_threshold, ), )
def solve_approx_lp_randomized( g: DFGraph, budget: int, seed_s: Optional[np.ndarray] = None, approx=True, time_limit: Optional[int] = None, write_log_file: Optional[PathLike] = None, print_to_console=True, write_model_file: Optional[PathLike] = None, eps_noise=0.01, solver_cores=os.cpu_count(), num_rounds=100, return_rounds=False, ): """Randomized rounding of LP relaxation Args: g: budget: seed_s: approx: time_limit: write_log_file: print_to_console: write_model_file: eps_noise: solver_cores: num_rounds: return_rounds: If True, return tuple (ScheduledResult, rounding_statistics) """ param_dict = { "LogToConsole": 1 if print_to_console else 0, "LogFile": str(write_log_file) if write_log_file is not None else "", "Threads": solver_cores, "TimeLimit": math.inf if time_limit is None else time_limit, "OptimalityTol": 1e-2 if approx else 1e-4, "IntFeasTol": 1e-3 if approx else 1e-5, "Presolve": 2, "StartNodeLimit": 10000000, } lpsolver = ILPSolverGurobi( g, int(0.9 * budget), # hack to get values under the budget gurobi_params=param_dict, seed_s=seed_s, solve_r=False, integral=False, eps_noise=eps_noise, write_model_file=write_model_file, ) lpsolver.build_model() try: r, s, u, free_e = lpsolver.solve() lp_feasible = True except ValueError as e: logging.exception(e) r, s, u, free_e = (None, None, None, None) lp_feasible = False best_solution = (float("inf"), None, None) rounding_cpus = [] rounding_activation_rams = [] rounding_in_budgets = [] if lp_feasible: # round the solution for i in range(num_rounds): s_ = (np.random.rand(*s.shape) <= s).astype(np.int32) r_ = solve_r_opt(g, s_) schedule, aux_data = schedule_from_rs(g, r_, s_) rounding_cpus.append(aux_data.cpu) rounding_activation_rams.append(aux_data.activation_ram) rounding_in_budgets.append(aux_data.activation_ram <= budget) if aux_data.activation_ram <= budget and ( best_solution[2] is None or aux_data.cpu <= best_solution[0]): best_solution = (aux_data.cpu, schedule, aux_data) if (i + 1) % 1 == 0: print( f"Rounded relaxation argmin {i+1} / num_rounds times, best cost {best_solution[0]}" ) schedule, aux_data = best_solution[1], best_solution[2] scheduled_result = ScheduledResult( solve_strategy=SolveStrategy.APPROX_RANDOMIZED_ROUND, solver_budget=budget, feasible=lp_feasible, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=lpsolver.solve_time, ilp_aux_data=ILPAuxData( U=u, Free_E=free_e, ilp_approx=approx, ilp_time_limit=time_limit, ilp_eps_noise=eps_noise, ilp_num_constraints=lpsolver.m.numConstrs, ilp_num_variables=lpsolver.m.numVars, approx_deterministic_round_threshold=None, ), ) if return_rounds: return ( scheduled_result, { "cpu": rounding_cpus, "activation_ram": rounding_activation_rams, "in_budget": rounding_in_budgets }, ) return scheduled_result
def solve_ilp_gurobi( g: DFGraph, budget: int, seed_s: Optional[np.ndarray] = None, approx=True, imposed_schedule: ImposedSchedule = ImposedSchedule.FULL_SCHEDULE, solve_r=True, time_limit: Optional[int] = None, write_log_file: Optional[PathLike] = None, print_to_console=True, write_model_file: Optional[PathLike] = None, eps_noise=0.01, solver_cores=os.cpu_count(), ): """ Memory-accurate solver with garbage collection. :param g: DFGraph -- graph definition extracted from model :param budget: int -- budget constraint for solving :param seed_s: np.ndarray -- optional parameter to set warm-start for solver, defaults to empty S :param approx: bool -- set true to return as soon as a solution is found that is within 1% of optimal :param imposed_schedule -- selects a set of constraints on R and S that impose a schedule or require some nodes to be computed :param solve_r -- if set, solve for the optimal R :param time_limit: int -- time limit for solving in seconds :param write_log_file: if set, log gurobi to this file :param print_to_console: if set, print gurobi logs to the console :param write_model_file: if set, write output model file to this location :param eps_noise: float -- if set, inject epsilon noise into objective weights, default 0.5% :param solver_cores: int -- if set, use this number of cores for ILP solving """ param_dict = { "LogToConsole": 1 if print_to_console else 0, "LogFile": str(write_log_file) if write_log_file is not None else "", "Threads": solver_cores, "TimeLimit": math.inf if time_limit is None else time_limit, "OptimalityTol": 1e-2 if approx else 1e-4, "IntFeasTol": 1e-3 if approx else 1e-5, "Presolve": 2, "StartNodeLimit": 10000000, } ilpsolver = ILPSolver( g, budget, gurobi_params=param_dict, seed_s=seed_s, eps_noise=eps_noise, imposed_schedule=imposed_schedule, solve_r=solve_r, write_model_file=write_model_file, ) ilpsolver.build_model() try: r, s, u, free_e = ilpsolver.solve() ilp_feasible = True except ValueError as e: logging.exception(e) r, s, u, free_e = (None, None, None, None) ilp_feasible = False ilp_aux_data = ILPAuxData( U=u, Free_E=free_e, ilp_approx=approx, ilp_time_limit=time_limit, ilp_eps_noise=eps_noise, ilp_num_constraints=ilpsolver.m.numConstrs, ilp_num_variables=ilpsolver.m.numVars, ilp_imposed_schedule=imposed_schedule, ) schedule, aux_data = schedule_from_rs(g, r, s) return ScheduledResult( solve_strategy=SolveStrategy.OPTIMAL_ILP_GC, solver_budget=budget, feasible=ilp_feasible, schedule=schedule, schedule_aux_data=aux_data, solve_time_s=ilpsolver.solve_time, ilp_aux_data=ilp_aux_data, )