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,
        )
示例#2
0
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,
    )
示例#4
0
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,
    )
示例#5
0
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,
    )
示例#6
0
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,
        ),
    )
示例#7
0
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,
        ),
    )
示例#8
0
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,
    )