def __init__(self, impt_reactions, tol=pow(10, -5), dec_cmp=5, dec_pnt=9, adjust_tol=True, bug_report=False): """ Keyword arguments: impt_reactions -- list of reactions of interest tol -- acceptable tolerance between points (default 10^-5) dec_cmp -- number of decimal places points are compared to (default 5) dec_pnt -- number of decimal places points are rounded to (default 9) adjust_tol -- boolean indicating whether adjustments can be made to the tolerance """ self.impt_reactions = impt_reactions self.impt_reactions_len = len(self.impt_reactions) # Optional parameters self.tol = tol self.dec_cmp = dec_cmp self.dec_pnt = dec_pnt self.adjust_tol = adjust_tol self.bug_report = bug_report # Set "Output" flag to 0 to prevent gurobi from printing license information and model statistics. self.env = gp.Env(empty=True) self.env.setParam("OutputFlag", 0) self.env.start() self.OPTIMAL = gp.GRB.OPTIMAL self.rebuild_model = True self.hull_id = 1 self.solver_count = 0 # Keep track of the number of models built
def test_gurobi_environment(self) -> None: """Tests that Gurobi environments can be passed to Model. Gurobi environments can include licensing and model parameter data. """ from cvxpy import GUROBI if GUROBI in INSTALLED_SOLVERS: import gurobipy # Set a few parameters to random values close to their defaults params = { 'MIPGap': np.random.random(), # range {0, INFINITY} 'AggFill': np.random.randint(10), # range {-1, MAXINT} 'PerturbValue': np.random.random(), # range: {0, INFINITY} } # Create a custom environment and set some parameters custom_env = gurobipy.Env() for k, v in params.items(): custom_env.setParam(k, v) # Testing QP Solver Interface sth = StandardTestLPs.test_lp_0(solver='GUROBI', env=custom_env) model = sth.prob.solver_stats.extra_stats for k, v in params.items(): # https://www.gurobi.com/documentation/9.1/refman/py_model_getparaminfo.html name, p_type, p_val, p_min, p_max, p_def = model.getParamInfo( k) self.assertEqual(v, p_val) else: with self.assertRaises(Exception) as cm: prob = Problem(Minimize(norm(self.x, 1)), [self.x == 0]) prob.solve(solver=GUROBI, TimeLimit=0) self.assertEqual(str(cm.exception), "The solver %s is not installed." % GUROBI)
def run(execute_sec: bool = False, execute_mtz: bool = False, execute_cb: bool = False): gurobi_env = gp.Env(empty=False) gurobi_env.setParam('MIPGap', 0.10) gurobi_env.setParam('OutputFlag', 0) generations = 100 pop_size = 30 number_cities = int( input('Please enter number of cities to be tested (At least 10): ')) results = {'Cities': []} if execute_sec: results['SEC'] = [] results['SEC_Time'] = [] if execute_cb: results['CB'] = [] results['CB_Time'] = [] if execute_mtz: results['MTZ'] = [] results['MTZ_Time'] = [] for i in tqdm(range(5, number_cities + 5, 5)): results['Cities'].append(i) city = City(number_cities=number_cities) cities = city.cities distance = city.pairwise_distance if execute_sec: sec_model = sec.SecModel(gurobi_env, cities, distance, None) sec_model.solve() results['SEC'].append(sec_model.optimal_gap) results['SEC_Time'].append(sec_model.time_elapsed) if execute_cb: cb_model = cb.CallBackModel(gurobi_env, cities, distance, startSolution=None, usingLazyConstraint=True) cb_model.solve() results['CB'].append(cb_model.optimal_gap) results['CB_Time'].append(cb_model.time_elapsed) if execute_mtz: start_solution = ga.solveGeneticAlgorithm(eliteSize=int( generations / 5), mutationRate=0.1, cities=cities, popSize=pop_size, generations=generations) mtz_model = mtz.MtzFormulation(gurobi_env, cities, distance, startSolution=start_solution, give_initial_solution=True) mtz_model.solve() results['MTZ'].append(mtz_model.optimal_gap) results['MTZ_Time'].append(mtz_model.time_elapsed) __drawGraph(results, execute_sec=execute_sec, execute_mtz=execute_mtz, execute_cb=execute_cb)
def optimize(queue, corr, comb, n_comb): try: env = gp.Env() m = gp.Model("m", env=env) m.Params.OutputFlag = 0 x = m.addMVar(shape=len(comb), vtype=GRB.BINARY, name="x") # # set goal m.setObjective(corr @ x, GRB.MAXIMIZE) # # add constraints # # x_u_v + x_v_t - x_u_t <= 1 for n in n_comb: f, s = n for i in range(len(comb)): if (s, i) in n_comb and (f, i) in n_comb: # print("(", n, ") + (", s, i, ") - (", f, i, ")" ) m.addConstr(x[n_comb.index(n)] + x[n_comb.index((s, i))] - x[n_comb.index((f, i))] <= 1) m.optimize() # convert results to list #print('Obj: %g' % m.objVal) res = [1 if v.x == 1 else 0 for v in m.getVars()] queue.put(res) m.dispose() env.dispose() gp.disposeDefaultEnv() #return list(m.getVars()), m, env except gp.GurobiError as e: print('Error code ' + str(e.errno) + ': ' + str(e)) except AttributeError: print('Encountered an attribute error')
def gl_gurobi(x0: np.ndarray, A: np.ndarray, b: np.ndarray, mu, opts): _, n = A.shape m, l = b.shape with gp.Env(empty=True) as env: env.setParam('OutputFlag', 0) env.start( ) # Build model m here with gp.Model('Gurobi', env=env) as M: # The default lower bound is 0. x = M.addMVar(shape=(n, l), name='x', lb=-gp.GRB.INFINITY) t2 = M.addMVar(shape=(n,), name='t2') z = M.addMVar(shape=b.shape, name='z', lb=-gp.GRB.INFINITY) cost = mu * t2.sum( ) for i in range(m): cost += z[ i, : ] @ z[ i, : ] * 0.5 M.setObjective(cost) M.addConstrs(z[ :, i ] + b[ :, i ] == A @ x[ :, i ] for i in range(l)) M.addConstrs(t2[ i ] @ t2[ i ] >= x[ i, : ] @ x[ i, : ] for i in range(n)) M.optimize( ) solve_time = M.Runtime fval = M.objVal num_iters = M.BarIterCount # Number of barrier iterations performed rv = x.X out = { "fval": fval, "tt": solve_time } return rv, num_iters, out
def base_model(open_cost, service_cost, verbose): env = gurobipy.Env() env.setParam("MIPGap", 1e-12) if not verbose: env.setParam("OutputFlag", 0) model = gurobipy.Model(env=env) plants = len(open_cost) customer = len(service_cost) service_vars = model.addVars( ((i, j) for j in range(plants) for i in range(customer)), vtype=gurobipy.GRB.BINARY, ) open_vars = model.addVars((j for j in range(plants)), vtype=gurobipy.GRB.BINARY) model.addConstrs( gurobipy.quicksum(service_vars[i, j] for j in range(plants)) == 1 for i in range(customer)) model.setObjective( (gurobipy.quicksum(open_cost[j] * open_vars[j] for j in range(plants)) + gurobipy.quicksum(service_cost[i][j] * service_vars[i, j] for i in range(customer) for j in range(plants))), sense=gurobipy.GRB.MINIMIZE, ) return model, service_vars, open_vars
def create_mcf_model( substrate_topology , flows , new_flow_source_node , new_flow_destination_node , new_flow_tx_rate): mcf_model = gp.Model("mcf-model", gp.Env("gurobi.log")) new_flow = Flow( source_node = new_flow_source_node , destination_node = new_flow_destination_node , flow_tx_rate = new_flow_tx_rate , paths = None , splitting_ratio = None ) potential_flow_set = flows + [new_flow] link_set = {link_tuple: link_idx for link_idx, link_tuple in enumerate(substrate_topology.edges)} # F : flow_idx # -> source_node # -> destination_node # -> flow on link (source_node, destination_node) F = {(flow_idx, source_node, destination_node): mcf_model.addVar(name="F{%d,%d,%d}" % (flow_idx, source_node, destination_node)) for flow_idx, _ in enumerate(potential_flow_set) for source_node, destination_node in link_set.keys()} for flow_idx, flow in enumerate(potential_flow_set): for u in substrate_topology.nodes: egress_flow = gp.LinExpr(0.0) ingress_flow = gp.LinExpr(0.0) for v in substrate_topology.neighbors(u): egress_flow += F[flow_idx, u, v] ingress_flow += F[flow_idx, v, u] if u == flow.source_node: # forall adjacent nodes sum == -1 mcf_model.addConstr((egress_flow - ingress_flow) == 1.0) elif u == flow.destination_node: mcf_model.addConstr((ingress_flow - egress_flow) == 1.0) else: # forall adjacent nodes sum == 0 mcf_model.addConstr((ingress_flow - egress_flow) == 0.0) U = {link_index: gp.LinExpr(0.0) for link_index in link_set.values()} for (flow_idx, source_node, destination_node) in F.keys(): link_index = link_set[source_node, destination_node] U[link_index] += (F[flow_idx, source_node, destination_node] * potential_flow_set[flow_idx].flow_tx_rate) # U[link_index] += F[flow_idx, source_node, destination_node] alpha = mcf_model.addVar(name="alpha", lb=0.0, ub=1.0) for u, v in {tuple(sorted(t_i)) for t_i in link_set.keys()}: one_direction = link_set[u, v] other_direction = link_set[v, u] mcf_model.addConstr((U[one_direction] + U[other_direction]) <= LINK_CAPACITY * alpha) mcf_model.setObjective(alpha, gp.GRB.MINIMIZE) return mcf_model, U, F
def gurobi_env(*args, **kwargs): ''' Return a gurobipy.Env object for use in constructing gurobipy.Model() objects. On an ordinary Python installation, this is a pass through to gurobipy.Env() :return: A gurobipy.Env object. ''' verify(gu, "gurobipy is not installed") if drm: return drm.gurobi_env() return gu.Env(*args, **kwargs)
def create_test_k_paths_model( target_topology , flows , new_source_node , new_destination_node , new_flow_tx_rate , k_disjoint_paths): path_allocation_model = gp.Model("path-allocation-model", gp.Env("gurobi.log")) new_flow = Flow( source_node = new_source_node , destination_node = new_destination_node , flow_tx_rate = new_flow_tx_rate , paths = k_disjoint_paths , splitting_ratio = None ) potential_flow_set = flows + [new_flow] X = {} for flow_index, flow in enumerate(potential_flow_set): flow_routing_constraint_variable = gp.LinExpr(0.0) for path_index, path in enumerate(flow.paths): X[flow_index, path_index] = path_allocation_model.addVar(name="X{%d,%d}" % (flow_index, path_index), lb=0.0, ub=1.0) flow_routing_constraint_variable += X[flow_index, path_index] path_allocation_model.addConstr(flow_routing_constraint_variable == 1.0) link_set = {tuple(sorted(link_tuple)): link_idx for link_idx, link_tuple in enumerate(target_topology.edges)} # Y: flow_index -> link_index -> portion of flow on link Y = {(flow_index, link_index): path_allocation_model.addVar(name="Y{%d,%d}" % (flow_index, link_index), lb=0.0, ub=1.0) for flow_index, flow in enumerate(potential_flow_set) for link_index in link_set.values()} Y_rhs = {(flow_index, link_index): gp.LinExpr(0.0) for flow_index, flow in enumerate(potential_flow_set) for link_index in link_set.values()} for flow_idnex, flow in enumerate(potential_flow_set): for path_index, path in enumerate(flow.paths): for link_tuple in nx.utils.pairwise(path): link_index = link_set[tuple(sorted(link_tuple))] Y_rhs[flow_index, link_index] += X[flow_index, path_index] * flow.flow_tx_rate for (flow_index, link_index), y_ik in Y_rhs.items(): path_allocation_model.addConstr(Y[flow_index, link_index] == y_ik) alpha = path_allocation_model.addVar("alpha", lb=0.0, ub=1.0) # U: link_index -> link_utilization U = {link_index: path_allocation_model.addVar(name="U{%s}" % str(link_index)) for link_index in link_set.values()} for (flow_index, link_index), y_ik in Y_rhs.items(): U[link_index] += y_ik for link_index, u_l in U.items(): path_allocation_model.addConstr(u_l <= (alpha * LINK_CAPACITY)) path_allocation_model.setObjective(alpha, gp.GRB.MINIMIZE) return path_allocation_model
def create_env(config): env = gurobipy.Env() if not config["LOG"]: env.setParam("OutputFlag", 0) for k, v in GUROBI_PARAMS.items(): def_val = DEFAULT_CONF[k] conf = config[k] if conf != def_val: env.setParam(v, conf) return env
def setupbatchenv(): env = gp.Env(empty=True) env.setParam('LogFile', 'batchmode.log') env.setParam('CSManager', 'http://localhost:61080') env.setParam('UserName', 'gurobi') env.setParam('ServerPassword', 'pass') env.setParam('CSBatchMode', 1) # No network communication happened up to this point. This will happen # once the caller invokes the start() method of the returned Env object. return env
def run_timing_gurobi(log_fn, lp_fn, method, network): nums = {'read_time': np.nan, 'presol_time': np.nan, 'sol_time': np.nan} env = grb.Env(log_fn) with timer(verbose=False) as t_read: model = grb.read(lp_fn, env) nums['read_time'] = t_read.usec / 1e6 model.setParam("LogToConsole", 0) model.setParam("Method", method) model.setParam("TimeLimit", 3 * 60 * 60) # noqa: E226 if method == 3: model.setParam("BarHomogeneous", 1) model.setParam("Threads", 4) nums['presol_time'] = 0 with timer(verbose=False) as t_solve: model.optimize() if model.Status == grb.GRB.OPTIMAL: nums['sol_time'] = t_solve.usec / 1e6 # Extract info from log file with open(log_fn) as log_fp: logs = log_fp.read() m = re.search(r"Presolve time: ([\d\.]+)s", logs) if m is not None: nums['presol_time'] = float(m.group(1)) nums['sol_time'] -= nums['presol_time'] m = re.search(r"Presolved: (\d+) rows, (\d+) columns, (\d+) nonzeros", logs) if m is not None: nums['pConstrs'] = int(m.group(1)) nums['pVars'] = int(m.group(2)) nums['pNZs'] = int(m.group(3)) else: nums['pConstrs'] = np.nan nums['pVars'] = np.nan nums['pNZs'] = np.nan m = re.search(r"Solved with ([a-z ]+)", logs) if m is not None: nums['solved_with'] = m.group(1) nums['Constrs'] = model.NumConstrs nums['Vars'] = model.NumVars nums['NZs'] = model.NumNZs nums['objective'] = getattr(model, 'ObjVal', np.nan) return nums
def __init__(self): super().__init__() self.status = LPStatus.UNKNOWN self.constraint_counter = 0 env = gb.Env() env.setParam(GRB.Param.OutputFlag, 0) env.start() self.model = gb.Model(env=env) # Maps a variable to a gurobi variable self.var_to_gurobi_var = {} # Maps a HIPS constraint to a gurobi constraint self.constr_to_gurobi_constraint = {} # Maps names to hips constraints self.name_to_hips_constraint = {}
def getGurobiEnvironment(config, retries=10): """ Create a new license environment Input: config: config file. Output: environment object Notes: if config["ENVIRONMENT"] is "GAM" it uses the free license. """ clogging.setup(syslog=True) logging.info("Creating environment...") os.environ[GRB_LICENSE_FILE] = os.path.expandvars( config[GUROBI][GUROBI_LIC]) cluster = config.get(ENVIRONMENT, CLUSTER_OPTION, fallback=CENSUS_CLUSTER) env = None rand_wait_base = np.random.uniform(1, 3) attempts = 0 success = False while (not success) and attempts < retries: try: if cluster == GAM_CLUSTER: env = gb.Env() else: logfile = os.path.expandvars( config[GUROBI][GUROBI_LOGFILE_NAME]) env1 = config[ENVIRONMENT][GRB_ISV_NAME] env2 = config[ENVIRONMENT][GRB_APP_NAME] env3 = int(config[ENVIRONMENT][GRB_ENV3]) env4 = config[ENVIRONMENT][GRB_ENV4].strip() env = gb.Env.OtherEnv(logfile, env1, env2, env3, env4) success = True except gb.GurobiError as err: attempts += 1 if attempts == retries: raise err rand_wait = 1.3**(attempts - 1) * rand_wait_base time.sleep(rand_wait / 1000) if cluster == GAM_CLUSTER: logging.debug("gurobi environment creation succeeded on attempt %s", attempts) else: logging.debug( "Successfully connected to Gurobi token server on attempt %s", attempts) return env
def build_model(graph: [(int, int)], costs: [int], verbose) -> gurobipy.Model: env = gurobipy.Env() env.setParam("MIPGap", 1e-12) if verbose: env.setParam("OutputFlag", 0) model = gurobipy.Model(env=env) node_vars = model.addVars((i for i in range(len(costs))), vtype=gurobipy.GRB.BINARY) for i, j in graph: model.addConstr(node_vars[i] + node_vars[j] >= 1) model.setObjective(gurobipy.quicksum( (node_vars[i] * costs[i] for i in range(len(costs)))), sense=gurobipy.GRB.MINIMIZE) model.update() return model
def exact_solution(items, capacity): env = gurobipy.Env() env.setParam("OutputFlag", 0) model = gurobipy.Model(env=env) item_vars = model.addVars((i for i in range(len(items))), vtype=gurobipy.GRB.BINARY) model.addConstr( gurobipy.quicksum(item_vars[i] * items[i][1] for i in range(len(items))) <= capacity) model.setObjective(gurobipy.quicksum(item_vars[i] * items[i][0] for i in range(len(items))), sense=gurobipy.GRB.MAXIMIZE) model.optimize() return model.objVal
def solve(problem: Union[ILP, KnapsackProblem]): """ MIP problem solver Parameters ---------- problem - prepared input variable of MIP problem Returns ------- Objective value """ # Create a new model with gp.Env(empty=True) as env: env.setParam('OutputFlag', 0) env.start() m = gp.Model("Estimation of right noncoverage", env=env) m.Params.LogToConsole = 0 try: _length = len(problem.get_f) x = m.addMVar(shape=(_length, ), lb=problem.get_lb[0], ub=problem.get_ub[0], vtype=GRB.BINARY) # Set objective obj = problem.get_f m.setObjective(obj @ x, GRB.MINIMIZE) if np.size(problem.get_ineq) != 0: m.addConstr(problem.get_ineq @ x <= problem.get_b, name="inequality") if np.size(problem.get_eq) != 0: m.addConstr(problem.get_eq @ x == problem.get_beq, name="equality") # Optimize model m.optimize() except gp.GurobiError as e: print(f'Error code :{e}') except AttributeError: print('Encountered an attribute error') return -1 * m.objVal
def create_env(config): """ Create a gurobi Env and set this with config options. :param config: a configuration to set the gurobi Env. :type config: dict :return: the gurobi env """ env = gurobipy.Env() if not config["LOG"]: env.setParam("OutputFlag", 0) for k, v in GUROBI_PARAMS.items(): def_val = DEFAULT_CONF[k] conf = config[k] if conf != def_val: env.setParam(v, conf) return env
temp.append(0) if t > tau and sum(temp[t - tau:t]) == tau: beta[p][q][n].append(1) else: beta[p][q][n].append(0) # Resource blocks attribution R = [] for bs in network: bw_per_rb = 12 * bs[ 'subcarrierSpacing'] #12 subcarriers per resouce block times 120kHz subcarrier spacing R.append(bw_per_rb * bs['resourceBlocks']) ### ----------- End of preprocessing phase --------------- ### Create environment and model optEnv = gb.Env('myEnv.log') optEnv.setParam('OutputFlag', 0) model = gb.Model('newModel', optEnv) ### Quadratic constraints control model.presolve().setParam(GRB.Param.PreQLinearize, 1) ### Add variables to the model x = [] y = [] #''' w = [] for m in range(m_bs): w.append([]) for n in range(n_ue): w[m].append([])
def solve(fuel_cost, Vessels, Insts, Times, Voys, instSetting, Name): # =============== INITIATE MODEL =============== Env = gp.Env(Name + ".log") model = gp.Model(name = Name, env = Env) model.setParam('TimeLimit', 3*60*60) # =============== SETS =============== # --------------- node_times --------------- node_times = [[[]for i in Insts]for v in Vessels] for v in Vessels: for i in Insts: for t in Times: count = 0 for j in Insts: for tau in Times: if fuel_cost[v][j][tau][i][t] != 0 or fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: node_times[v][i].append(t) # --------------- node_vessels --------------- node_vessels = [[[]for t in Times] for i in Insts] for i in Insts: for t in Times: for v in Vessels: count = 0 for j in Insts: for tau in Times: if fuel_cost[v][j][tau][i][t] != 0 or fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: node_vessels[i][t].append(v) # --------------- to_insts --------------- to_insts = [[[[]for t in Times]for i in Insts]for v in Vessels] # tror denne er riktig for v in Vessels: for i in Insts: for t in node_times[v][i]: for j in Insts: count = 0 for tau in Times: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count > 0: to_insts[v][i][t].append(j) # --------------- from_insts --------------- from_insts = [[[[]for tau in Times]for j in Insts]for v in Vessels] for v in Vessels: for j in Insts: for tau in node_times[v][j]: for i in Insts: count = 0 for t in Times: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count > 0: from_insts[v][j][tau].append(i) # --------------- departure_times --------------- departure_times = [[[[]for j in Insts] for i in Insts] for v in Vessels] #ser riktig ut for v in Vessels: for i in Insts: for j in Insts: for t in Times: count = 0 for tau in Times: #kan evt skrive for tau større enn t (for å øke leseligheten) if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: departure_times[v][i][j].append(t) # --------------- arrival_times --------------- arrival_times = [[[[]for j in Insts] for i in Insts] for v in Vessels] for v in Vessels: for i in Insts: for j in Insts: for tau in Times: count = 0 for t in Times: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: arrival_times[v][i][j].append(tau) # --------------- specific_departure_times --------------- specific_departure_times = [[[[[] for tau in Times] for j in Insts] for i in Insts] for v in Vessels] for v in Vessels: for i in Insts: for j in Insts: for tau in arrival_times[v][i][j]: for t in Times: if fuel_cost[v][i][t][j][tau] != 0: specific_departure_times[v][i][j][tau].append(t) # --------------- specific_arrival_times --------------- specific_arrival_times = [[[[[] for j in Insts] for t in Times] for i in Insts] for v in Vessels] for v in Vessels: for i in Insts: for j in Insts: for t in departure_times[v][i][j]: for tau in Times: if fuel_cost[v][i][t][j][tau] != 0: specific_arrival_times[v][i][t][j].append(tau) # --------------- symmetric_vessels --------------- symmetric_vessels = [[0 for v2 in Vessels] for v1 in Vessels] for v1 in Vessels: for v2 in Vessels: if data.AvaliableTime[v1] == data.AvaliableTime[v2]: symmetric_vessels[v1][v2] = 1 # =============== PARAMETERS =============== Demand = data.Demand[instSetting] DemandNum = data.DemandNum[instSetting] VesselCap = data.VesselCap # =============== VARIABLES =============== x = {} for v in Vessels: for m in Voys: for i in Insts: for j in Insts: if j != i: for t in departure_times[v][i][j]: for tau in specific_arrival_times[v][i][t][j]: x[v,i,t,j,tau,m] = model.addVar(vtype=gp.GRB.BINARY, name=("x_" + str(v) + "_" + str(m) + "_" + str(i) + "_" + str(t) + "_" + str(j) + "_" + str(tau))) # a = [[0 for tau in Times]for j in Insts] # # for j in Insts: # for tau in Times: # a[j][tau] = model.addVar(vtype=gp.GRB.INTEGER, name=("a_" + str(j) + "_" + str(tau))) # print("\rGenerating variables: %d%% "%math.ceil(counter*100/(np.size(Vessels)*np.size(Insts))), end="\r", flush = True) # counter += 1 # =============== MODEL UPDATE =============== model.update() # =============== CONSTRAINTS =============== # --------------- Flow conservation --------------- model.addConstrs(( gp.quicksum( x[v,j,tau,i,t,m] for j in from_insts[v][i][t] for tau in specific_departure_times[v][j][i][t]) - gp.quicksum( x[v,i,t,j,tau,m] for j in to_insts[v][i][t] for tau in specific_arrival_times[v][i][t][j]) == 0 for v in Vessels for i in Insts if i != 0 for t in node_times[v][i] for m in Voys) , "Flow_Conservation:_v" + str(v) + " i" + str(i) + " t" + str(t) + " m" + str(m)) # --------------- Any installation can only be visited once per voyage --------------- model.addConstrs(( gp.quicksum( x[v,i,t,j,tau,m] for i in Insts if i != j for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]) <= 1 for j in Insts for v in Vessels for m in Voys) , "Only one Inst visit per voy: j" + str(j) + " v" + str(v) + " m" + str(m)) # --------------- Evry PSV can only sail from the depot once per voyage --------------- model.addConstrs(( gp.quicksum( x[v,0,t,j,tau,m] for j in Insts for t in departure_times[v][0][j] for tau in specific_arrival_times[v][0][t][j]) <= 1 for v in Vessels for m in Voys) , "Only sail from depot once per voyage: v" + str(v) + " m" + str(m)) # --------------- Next voyage must start after the last one --------------- model.addConstrs(( gp.quicksum( tau * x[v,i,t,0,tau,m-1] for i in Insts for t in departure_times[v][i][0] for tau in specific_arrival_times[v][i][t][0]) + 300 * (1 - gp.quicksum( x[v,i,t,0,tau,m-1] for i in Insts for t in departure_times[v][i][0] for tau in specific_arrival_times[v][i][t][0])) - gp.quicksum( t * x[v,0,t,j,tau,m] for j in Insts for t in departure_times[v][0][j] for tau in specific_arrival_times[v][0][t][j]) - 300 * (1 - gp.quicksum( x[v,0,t,j,tau,m] for j in Insts for t in departure_times[v][0][j] for tau in specific_arrival_times[v][0][t][j])) <= 0 for v in Vessels for m in Voys if m != 0) , "Next voyage must start after current voyage: v" + str(v) + " m" + str(m)) # --------------- All service jobs must be performed --------------- model.addConstrs(( gp.quicksum( x[v,i,t,j,tau,m] for v in Vessels for i in Insts if i != j for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j] for m in Voys) == DemandNum[j] for j in Insts if j != 0) , name = ("Demanded visits: j" + str(j))) # --------------- PSV capacity --------------- model.addConstrs(( gp.quicksum( x[v,i,t,j,tau,m] * Demand[j] for i in Insts for j in Insts if j != 0 for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]) <= VesselCap[v] for v in Vessels for m in Voys) , "PSV capacity: v" + str(v) + " m" + str(m)) # --------------- Spread of arrivals --------------- if data.spreadTime > 0: model.addConstrs(( gp.quicksum( x[v,i,t,j,tau,m] for v in Vessels for m in Voys for i in Insts for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j] if t2 - tau <= data.spreadTime if tau - t2 <= 0) <= 1 for j in Insts for t2 in Times) , "Spread of arrivals:") # --------------- Symmetry Breaking --------------- # model.addConstrs(( # # gp.quicksum( # # x[v1][i][t][j][tau][1] * fuel_cost[v1][i][t][j][tau] # # for i in Insts # for j in Insts # for t in departure_times[v1][i][j] # for tau in specific_arrival_times[v1][i][t][j]) # # + (1 - gp.quicksum( # # x[v1][0][t][j][tau][1] # # for j in Insts # for t in departure_times[v1][0][j] # for tau in specific_arrival_times[v1][0][t][j])) # # - gp.quicksum( # # x[v2][i][t][j][tau][1] * fuel_cost[v2][i][t][j][tau] # # for i in Insts # for j in Insts # for t in departure_times[v2][i][j] # for tau in specific_arrival_times[v2][i][t][j]) # # >= 0.01 # # for v1 in Vessels # for v2 in Vessels # if v1 < v2 # if symmetric_vessels[v1][v2] == 1) # # , "Symmetry Breaking:") # =============== MODEL UPDATE =============== model.update() # =============== OBJECTIVE =============== model.setObjective( gp.quicksum(x[v,i,t,j,tau,m] * fuel_cost[v][i][t][j][tau] for v in Vessels for m in Voys for i in Insts for j in Insts if j != i for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]), gp.GRB.MINIMIZE) # =============== MODEL UPDATE =============== model.update() model.printStats() model.write(Name + ".lp") # =============== RUN MODEL =============== model.optimize() model.printAttr('x') solEdges = [[[[[0 for tau in Times]for j in Insts]for t in Times]for i in Insts]for v in Vessels] for a in model.getVars(): if a.varName[0] == 'x' and a.x == 1: temp = a.varName.split('_') solEdges[int(temp[1])][int(temp[3])][int(temp[4])][int(temp[5])][int(temp[6])] = 1 tot = 0 for v in Vessels: for i in Insts: for t in Times: for j in Insts: for tau in Times: tot += solEdges[v][i][t][j][tau]*fuel_cost[v][i][t][j][tau] print(tot)
def fast_inner_ball(A, b): """A Python function to compute the maximum inscribed ball in the given polytope using gurobi LP solver Returns the optimal solution for the following linear program: max r, subject to, a_ix + r||a_i|| <= b, i=1,...,n Keyword arguments: A -- an mxn matrix that contains the normal vectors of the facets of the polytope row-wise b -- a m-dimensional vector """ extra_column = [] m = A.shape[0] n = A.shape[1] for i in range(A.shape[0]): entry = np.linalg.norm(A[i, ]) extra_column.append(entry) column = np.asarray(extra_column) A_expand = np.c_[A, column] with gp.Env(empty=True) as env: env.setParam("OutputFlag", 0) env.start() d = A_expand.shape[1] with gp.Model(env=env) as model: # Create variable x = model.addMVar( shape=d, vtype=GRB.CONTINUOUS, name="x", lb=-GRB.INFINITY, ub=GRB.INFINITY, ) model.update() # Make A_full_dim sparse A_expand_sparse = sp.csr_matrix(A_expand.astype("float")) # Add constraints model.addMConstr(A_expand_sparse, x, "<", b, name="c") model.update() # Set the ith row of the A matrix as the objective function a = np.ones((1, n + 1)) azero = np.zeros((1, n)) a[:, :-1] = azero objective_function = a[0] # Set the objective function in the model model.setMObjective(None, objective_function, 0.0, None, None, x, GRB.MAXIMIZE) model.update() # Optimize model model.optimize() # Get the solution returned varss = model.getVars() # Get the center point and the radius of max ball from the solution of LP; its last element is the radius point = [] for i in range(len(varss)): if i == len(varss) - 1: r = varss[i].x else: value = varss[i].x point.append(value) # And check whether the computed radius is negative if r < 0: print( "The radius calculated has negative value. The polytope is infeasible or something went wrong with the solver" ) else: return point, r
def fast_remove_redundant_facets(lb, ub, S, c, opt_percentage=100): """A function to find and remove the redundant facets and to find the facets with very small offset and to set them as equalities Keyword arguments: lb -- lower bounds for the fluxes, i.e., a n-dimensional vector ub -- upper bounds for the fluxes, i.e., a n-dimensional vector S -- the mxn stoichiometric matrix, s.t. Sv = 0 c -- the objective function to maximize opt_percentage -- consider solutions that give you at least a certain percentage of the optimal solution (default is to consider optimal solutions only) """ if lb.size != S.shape[1] or ub.size != S.shape[1]: raise Exception( "The number of reactions must be equal to the number of given flux bounds." ) # declare the tolerance that gurobi works properly (we found it experimentally) redundant_facet_tol = 1e-07 tol = 1e-06 m = S.shape[0] n = S.shape[1] beq = np.zeros(m) Aeq_res = S A = np.zeros((2 * n, n), dtype="float") A[0:n] = np.eye(n) A[n:] -= np.eye(n, n, dtype="float") b = np.concatenate((ub, -lb), axis=0) b = np.asarray(b, dtype="float") b = np.ascontiguousarray(b, dtype="float") # call fba to obtain an optimal solution max_biomass_flux_vector, max_biomass_objective = fast_fba(lb, ub, S, c) val = -np.floor(max_biomass_objective / tol) * tol * opt_percentage / 100 b_res = [] A_res = np.empty((0, n), float) beq_res = np.array(beq) try: # To avoid printint the output of the optimize() function of Gurobi, we need to set an environment like this with gp.Env(empty=True) as env: env.setParam("OutputFlag", 0) env.start() with gp.Model(env=env) as model: # Create variables x = model.addMVar( shape=n, vtype=GRB.CONTINUOUS, name="x", lb=lb, ub=ub, ) # Make sparse Aeq Aeq_sparse = sp.csr_matrix(S) # Make A sparse A_sparse = sp.csr_matrix(np.array(-c)) b_sparse = np.array(val) # Set the b and beq vectors as numpy vectors b = np.array(b) beq = np.array(beq) # Add constraints model.addMConstr(Aeq_sparse, x, "=", beq, name="c") # Update the model to include the constraints added model.update() # Add constraints for the uneqalities of A model.addMConstr(A_sparse, x, "<", [val], name="d") # Update the model with the extra constraints and then print it model.update() model_iter = model.copy() # initialize indices_iter = range(n) removed = 1 offset = 1 facet_left_removed = np.zeros((1, n), dtype=bool) facet_right_removed = np.zeros((1, n), dtype=bool) # Loop until nor redundant facets are found while removed > 0 or offset > 0: removed = 0 offset = 0 indices = indices_iter indices_iter = [] Aeq_sparse = sp.csr_matrix(Aeq_res) beq = np.array(beq_res) b_res = [] A_res = np.empty((0, n), float) for i in indices: # Set the ith row of the A matrix as the objective function objective_function = A[i, ] redundant_facet_right = True redundant_facet_left = True # for the maximum objective_function_max = np.asarray( [-x for x in objective_function]) model_iter = update_model( model_iter, n, Aeq_sparse, beq, lb, ub, A_sparse, [val], objective_function_max, ) model_iter.optimize() # Again if optimized status = model_iter.status if status == GRB.OPTIMAL: # Get the max objective value max_objective = -model_iter.getObjective( ).getValue() else: max_objective = ub[i] # if this facet was not removed in a previous iteration if not facet_right_removed[0, i]: ub_iter = ub.copy() ub_iter[i] = ub_iter[i] + 1 model_iter = update_model( model_iter, n, Aeq_sparse, beq, lb, ub_iter, A_sparse, [val], objective_function_max, ) model_iter.optimize() status = model_iter.status if status == GRB.OPTIMAL: # Get the max objective value with relaxed inequality max_objective2 = -model_iter.getObjective( ).getValue() if (np.abs(max_objective2 - max_objective) > redundant_facet_tol): redundant_facet_right = False else: removed += 1 facet_right_removed[0, i] = True model_iter = update_model( model_iter, n, Aeq_sparse, beq, lb, ub, A_sparse, [val], objective_function, ) model_iter.optimize() # If optimized status = model_iter.status if status == GRB.OPTIMAL: # Get the min objective value min_objective = model_iter.getObjective().getValue( ) else: min_objective = lb[i] # if this facet was not removed in a previous iteration if not facet_left_removed[0, i]: lb_iter = lb.copy() lb_iter[i] = lb_iter[i] - 1 model_iter = update_model( model_iter, n, Aeq_sparse, beq, lb_iter, ub, A_sparse, [val], objective_function, ) model_iter.optimize() status = model_iter.status if status == GRB.OPTIMAL: # Get the min objective value with relaxed inequality min_objective2 = model_iter.getObjective( ).getValue() if (np.abs(min_objective2 - min_objective) > redundant_facet_tol): redundant_facet_left = False else: removed += 1 facet_left_removed[0, i] = True if (not redundant_facet_left) or ( not redundant_facet_right): width = abs(max_objective - min_objective) # Check whether the offset in this dimension is small (and set an equality) if width < redundant_facet_tol: offset += 1 Aeq_res = np.vstack(( Aeq_res, A[i, ], )) beq_res = np.append( beq_res, min(max_objective, min_objective)) # Remove the bounds on this dimension ub[i] = sys.float_info.max lb[i] = -sys.float_info.max else: # store this dimension indices_iter.append(i) if not redundant_facet_left: # Not a redundant inequality A_res = np.append( A_res, np.array([A[n + i, ]]), axis=0, ) b_res.append(b[n + i]) else: lb[i] = -sys.float_info.max if not redundant_facet_right: # Not a redundant inequality A_res = np.append( A_res, np.array([A[i, ]]), axis=0, ) b_res.append(b[i]) else: ub[i] = sys.float_info.max else: # Remove the bounds on this dimension ub[i] = sys.float_info.max lb[i] = -sys.float_info.max b_res = np.asarray(b_res) A_res = np.asarray(A_res, dtype="float") A_res = np.ascontiguousarray(A_res, dtype="float") return ( A_res, b_res, Aeq_res, beq_res, ) # Print error messages except gp.GurobiError as e: print("Error code " + str(e.errno) + ": " + str(e)) except AttributeError: print("Gurobi solver failed.")
def SolveReducedLinearProblemGurobiPy(args, rhoF = None, rhoS = None, \ console_output = None, logs_on = None): """ Sets up and solves the linear form of the food security problem. Parameters ---------- args : dict Dictionary of arguments needed as model input (as given by SetParameters()). rhoF : float or None The penalty for shortcomings of the food demand. If None the values in args are used. (This is used from within the GetPenalties function to easily change penalties while keeping other args the same.) rhoS : float or None The penalty for insolvency. If None the values in args are used. (This is used from within the GetPenalties function to easily change penalties while keeping other args the same.) console_output : boolean, optional Specifying whether the progress should be documented thorugh console outputs. The default is defined in ModelCode/GeneralSettings. logs_on : boolean, optional Specifying whether the progress should be documented in a log document. The default is defined in ModelCode/GeneralSettings. Returns ------- status : int status of solver (optimal: 2) crop_alloc : np.array gives the optimal crop areas for all years, crops, clusters meta_sol : dict additional information about the model output prob : gurobi model The food security model that was set up. durations : list time for setting up the model, time for solving, and total time (in sec.) """ def _flatten(ListOfLists): return(list(it.chain(*ListOfLists))) if rhoF is None: rhoF = args["rhoF"] if rhoS is None: rhoS = args["rhoS"] _printing("\nSolving Model", console_output = console_output, logs_on = logs_on) start = tm.time() # no output to console from solver env = gp.Env(empty = True) env.setParam('OutputFlag', 0) env.start() # intitialize stochastic optimization problem prob = gp.Model("SustainableFoodSecurity", env = env) # get dimensions T = args["T"] K = len(args["k_using"]) J = args["num_crops"] N = args["N"] termyear_p1 = args["terminal_years"] + 1 termyear_p1[termyear_p1 == 0] = T termyear_p1 = termyear_p1.astype(int) # index tupels for variables and constraints indVfood = _flatten([[(t, s) for t in range(0, termyear_p1[s])] \ for s in range(0, N)]) indW = _flatten(_flatten([[[(t, k, s) for t in range(0, termyear_p1[s])] \ for k in range(0,K)] for s in range(0, N)])) indCultCosts = _flatten([[(t, j, k) for (t,j,k) in \ it.product(range(0,termyear_p1[s]), range(0, J), range(0, K))] \ for s in range(0, N)]) indMaxArea = list(it.product(range(0, K), range(0, T))) indCropsClusters = list(it.product(range(0, J), range(0, K))) # variables x = prob.addVars(range(0, T), range(0, J), range(0, K), name = "x") Vfood = prob.addVars(indVfood, name = "Vfood") Vsol = prob.addVars(range(0, N), name = "Vsol") Wgov = prob.addVars(indW, name = "Wgov") # objective function obj = gp.quicksum([1/N * x[t,j,k] * args["costs"][j,k] \ for (t,j,k) in indCultCosts] + \ [1/N * rhoF * Vfood[t, s] for (t, s) in indVfood] + \ [1/N * rhoS * Vsol[s] for s in range(0, N)] + \ [0 * Wgov[t, k, s] for (t, k, s) in indW]) prob.setObjective(obj, gp.GRB.MINIMIZE) # constraints 1 prob.addConstrs((gp.quicksum([x[t, j, k] for j in range(0, J)]) \ <= args["max_areas"][k] for (k, t) in indMaxArea), "c_marea") # constraints 2 prob.addConstrs((gp.quicksum([Vfood[t, s]] + \ [args["ylds"][s, t, j, k] * x[t, j, k] * args["crop_cal"][j] \ for (j, k) in indCropsClusters]) \ >= (args["demand"][t] - args["import"]) \ for (t, s) in indVfood), "c_demand") # constraints 3 prob.addConstrs((gp.quicksum([-Vsol[s]] + \ [- args["tax"] * (args["ylds"][s, t, j, k] * \ x[t, j, k] * args["prices"][j, k] - \ x[t, j, k] * args["costs"][j, k]) \ for (j, t, k) in it.product(range(0, J), \ range(0, termyear_p1[s]), range(0, K))] + \ [args["cat_clusters"][s, t, k] * (1 - args["tax"]) * Wgov[t, k, s] \ for (t, k) in it.product(range(0, termyear_p1[s]), \ range(0, K))]) \ <= args["ini_fund"] for s in range(0, N)), "c_sol") # constraints 4 prob.addConstrs((gp.quicksum([- Wgov[t, k, s]] + \ [- args["ylds"][s, t, j, k] * x[t, j, k] * args["prices"][j, k] + \ x[t, j, k] * args["costs"][j, k] for j in range(0, J)]) \ <= - args["guaranteed_income"][t, k] for (t, k, s) in indW), "c_gov") # solving middle = tm.time() prob.optimize() end = tm.time() status = prob.status # calculate durations durationBuild = middle - start durationSolve = end - middle durationTotal = end - start durations = [durationBuild, durationSolve, durationTotal] # get results crop_alloc = np.zeros((T, J, K)) meta_sol = [] if status != 2: warn.warn("Non-optimal status of solver") return(status, crop_alloc, meta_sol, prob, durations) else: for t in range(0, T): for j in range(0, J): for k in range(0, K): crop_alloc[t, j, k] = prob.getVarByName("x[" + str(t) + \ "," + str(j) + "," + str(k) + "]").X meta_sol = GetMetaInformation(crop_alloc, args, rhoF, rhoS) _printing(" Time Setting up model: " + \ str(np.round(durations[0], 2)) + "s", console_output = console_output, logs_on = logs_on) _printing(" Solving model: " + \ str(np.round(durations[1], 2)) + "s", console_output = console_output, logs_on = logs_on) _printing(" Total: " + \ str(np.round(durations[2], 2)) + "s", console_output = console_output, logs_on = logs_on) return(status, crop_alloc, meta_sol, prob, durations)
def solve(self): self.start_solving = datetime.datetime.now(datetime.timezone.utc) if self.mode == 'cplex': c = cplex.Cplex() c.parameters.threads.set(self.threads) if self.stdout == os.devnull: c.set_results_stream(open(os.devnull, 'w')) c.set_log_stream(open(os.devnull, 'w')) elif self.stdout == sys.stdout: c.set_results_stream(sys.stdout) c.set_log_stream(sys.stdout) elif self.stdout == 'log': out = open(self.filename + '.log', 'w') c.set_results_stream(out) c.set_log_stream(out) c.read(self.filename + '.lp') try: c.solve() self.end_solving = datetime.datetime.now(datetime.timezone.utc) except CplexSolverError: print("Exception raised during solve") return None status = c.solution.get_status() if status == c.solution.status.unbounded: print("Model is unbounded") return None if status == c.solution.status.infeasible: print("Model is infeasible") return None if status == c.solution.status.infeasible_or_unbounded: print("Model is infeasible or unbounded") return None self.variables = {} self.variables['objective_value'] = c.solution.get_objective_value( ) if status == c.solution.status.optimal or status == c.solution.status.MIP_optimal: print('Model solved successfully!') for name, value in zip(c.variables.get_names(), c.solution.get_values()): if ' ' + name + ' ' in self.binary or ' ' + name + ' ' in self.integer: self.variables[name] = int(np.rint(value)) else: self.variables[name] = value return self.variables elif self.mode == 'gurobi': gurobi_env = grb.Env() gurobi_env.setParam('Threads', self.threads) if self.stdout == os.devnull: gurobi_env.setParam('OutputFlag', 0) elif self.stdout == sys.stdout: gurobi_env.setParam('OutputFlag', 1) elif self.stdout == 'log': gurobi_env.setParam('OutputFlag', 0) gurobi_env.setParam('LogFile', self.filename + '.log') model = grb.read(self.filename + '.lp', gurobi_env) model.optimize() self.end_solving = datetime.datetime.now(datetime.timezone.utc) if model.status == grb.GRB.Status.INFEASIBLE: print('Optimization was stopped with status %d' % model.status, 'infeasible') return None elif model.status == grb.GRB.Status.OPTIMAL: print('model solved successfully') self.variables = {} solution_vars = model.getVars() # print('solution vars', len(solution_vars)) for var in solution_vars: if ' ' + name + ' ' in self.binary or ' ' + name + ' ' in self.integer: self.variables[var.varName] = int(np.rint(var.x)) else: self.variables[var.varName] = var.x return self.variables else: print('model was not optimized') return None elif self.mode == 'lpsolve': lp = lpsolve('read_lp_file', self.filename + '.lp') lpsolve('set_lp_name', lp, self.name) status = lpsolve('solve', lp) self.end_solving = datetime.datetime.now(datetime.timezone.utc) if status == 3: print("Model is unbounded") return if status == 2: print("Model is infeasible") return if status == 4: print("The model is degenerative") return if status == -2: print("Out of memory") return if status == 1: print("The model is sub-optimal") return if status == 4: print("The model is degenerative") return if status == 5: print("Numerical failure encountered") return if status == 25: print("Accuracy error encountered") return self.variables = {} for name, value in zip(lpsolve('get_col_names', lp), lpsolve('get_solution', lp)[1]): if ' ' + name + ' ' in self.binary or ' ' + name + ' ' in self.integer: self.variables[name] = int(np.rint(value)) else: self.variables[name] = value lpsolve('delete_lp', lp) return self.variables
def solve(fuel_cost, Name, scenario_number): # =============== INITIATE MODEL =============== Env = gp.Env(Name + ".log") model = gp.Model(name=Name, env=Env) model.setParam('TimeLimit', 3 * 60 * 60) # =============== SETS =============== vessels, vessel_numbers = d.get_vessels_in_scenario(scenario_number) orders, order_numbers = d.get_orders_in_scenario(scenario_number) # --------------- node_times --------------- node_times = [[[] for i in order_numbers] for v in vessel_numbers] for v in vessel_numbers: for i in order_numbers: for t in d.time_periods: count = 0 for j in order_numbers: for tau in d.time_periods: if fuel_cost[v][j][tau][i][t] != 0 or fuel_cost[v][i][ t][j][tau] != 0: count += 1 if count != 0: node_times[v][i].append(t) # --------------- node_vessels --------------- node_vessels = [[[] for t in d.time_periods] for i in order_numbers] for i in order_numbers: for t in d.time_periods: for v in vessel_numbers: count = 0 for j in order_numbers: for tau in d.time_periods: if fuel_cost[v][j][tau][i][t] != 0 or fuel_cost[v][i][ t][j][tau] != 0: count += 1 if count != 0: node_vessels[i][t].append(v) # --------------- to_insts --------------- to_insts = [[[[] for t in d.time_periods] for i in order_numbers] for v in vessel_numbers] # tror denne er riktig for v in vessel_numbers: for i in order_numbers: for t in node_times[v][i]: for j in order_numbers: count = 0 for tau in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count > 0: to_insts[v][i][t].append(j) # --------------- from_insts --------------- from_insts = [[[[] for tau in d.time_periods] for j in order_numbers] for v in vessel_numbers] for v in vessel_numbers: for j in order_numbers: for tau in node_times[v][j]: for i in order_numbers: count = 0 for t in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count > 0: from_insts[v][j][tau].append(i) # --------------- departure_times --------------- departure_times = [[[[] for j in order_numbers] for i in order_numbers] for v in vessel_numbers] #ser riktig ut for v in vessel_numbers: for i in order_numbers: for j in order_numbers: for t in d.time_periods: count = 0 for tau in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: departure_times[v][i][j].append(t) # --------------- arrival_times --------------- arrival_times = [[[[] for j in order_numbers] for i in order_numbers] for v in vessel_numbers] for v in vessel_numbers: for i in order_numbers: for j in order_numbers: for tau in d.time_periods: count = 0 for t in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: count += 1 if count != 0: arrival_times[v][i][j].append(tau) # --------------- specific_departure_times --------------- specific_departure_times = [[[[[] for tau in d.time_periods] for j in order_numbers] for i in order_numbers] for v in vessel_numbers] for v in vessel_numbers: for i in order_numbers: for j in order_numbers: for tau in arrival_times[v][i][j]: for t in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: specific_departure_times[v][i][j][tau].append(t) # --------------- specific_arrival_times --------------- specific_arrival_times = [[[[[] for j in order_numbers] for t in d.time_periods] for i in order_numbers] for v in vessel_numbers] for v in vessel_numbers: for i in order_numbers: for j in order_numbers: for t in departure_times[v][i][j]: for tau in d.time_periods: if fuel_cost[v][i][t][j][tau] != 0: specific_arrival_times[v][i][t][j].append(tau) # --------------- symmetric_vessels --------------- # =============== PARAMETERS =============== # =============== VARIABLES =============== x = {} for v in vessel_numbers: for i in order_numbers: for j in order_numbers: if j != i: for t in departure_times[v][i][j]: for tau in specific_arrival_times[v][i][t][j]: x[v, i, t, j, tau] = model.addVar( vtype=gp.GRB.BINARY, name=("x_" + str(v) + "_" + str(i) + "_" + str(t) + "_" + str(j) + "_" + str(tau))) # a = [[0 for tau in Times]for j in Insts] # # for j in Insts: # for tau in Times: # a[j][tau] = model.addVar(vtype=gp.GRB.INTEGER, name=("a_" + str(j) + "_" + str(tau))) # print("\rGenerating variables: %d%% "%math.ceil(counter*100/(np.size(Vessels)*np.size(Insts))), end="\r", flush = True) # counter += 1 # =============== MODEL UPDATE =============== model.update() # =============== CONSTRAINTS =============== # --------------- Flow conservation --------------- model.addConstrs( (gp.quicksum(x[v, j, tau, i, t] for j in from_insts[v][i][t] for tau in specific_departure_times[v][j][i][t]) - gp.quicksum(x[v, i, t, j, tau] for j in to_insts[v][i][t] for tau in specific_arrival_times[v][i][t][j]) == 0 for v in vessel_numbers for i in order_numbers if i != 0 for t in node_times[v][i]), "Flow_Conservation:_v" + str(v) + " i" + str(i) + " t" + str(t)) # --------------- Any installation can only be visited once per voyage --------------- model.addConstrs( (gp.quicksum(x[v, i, t, j, tau] for i in order_numbers if i != j for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]) <= 1 for j in order_numbers for v in vessel_numbers), "Only one Inst visit per voy: j" + str(j) + " v" + str(v)) # --------------- Evry PSV can only sail from the depot once per voyage --------------- model.addConstrs( (gp.quicksum(x[v, 0, t, j, tau] for j in order_numbers for t in departure_times[v][0][j] for tau in specific_arrival_times[v][0][t][j]) <= 1 for v in vessel_numbers), "Only sail from depot once per voyage: v" + str(v)) # --------------- All service jobs must be performed --------------- model.addConstrs( (gp.quicksum(x[v, i, t, j, tau] for v in vessel_numbers for i in order_numbers if i != j for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]) == 1 for j in order_numbers if j != 0), name=("Demanded visits: j" + str(j))) # --------------- PSV capacity --------------- model.addConstrs((gp.quicksum( x[v, i, t, j, tau] * orders[j].demand for i in order_numbers for j in order_numbers if j != 0 for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]) <= vessels[v].capacity for v in vessel_numbers), "PSV capacity: v" + str(v)) # =============== MODEL UPDATE =============== model.update() # =============== OBJECTIVE =============== model.setObjective( gp.quicksum(x[v, i, t, j, tau] * fuel_cost[v][i][t][j][tau] for v in vessel_numbers for i in order_numbers for j in order_numbers if j != i for t in departure_times[v][i][j] for tau in specific_arrival_times[v][i][t][j]), gp.GRB.MINIMIZE) # =============== MODEL UPDATE =============== model.update() model.printStats() # =============== RUN MODEL =============== model.optimize() model.printAttr('x')
def k_tsp(K, n, dist, relaxed=False): ''' Função que define e resolve o modelo exato ou relaxado para o K-TSP, dada uma determinada instância. Aqui, K-TSP generaliza o TSP e o 2-TSP para qualquer K, o que evita a implementação de modelos diferentes. Args: K: nº de caixeiros viajantes. n: nº de vértices do grafo. dist: dicionário de custo das arestas (i,j), i >= j. relaxed: booleano que indica se será resolvido o modelo original ou a relaxação lagrangiana. Returns: Dicionário da solução, contendo a solução ótima se resolvido o problema original ou os melhores limitantes se resolvida a relaxação lagrangiana. ''' # Inicializar ambiente env = gp.Env(empty = True) env.setParam('OutputFlag', 0) env.start() # Inicializar modelo model = gp.Model(name = str(K) + '-tsp', env = env) # Adaptar o dicionário de distâncias de acordo com a quantidade de # caixeiros distK = { (i, j, k): dist[i, j] for i in range(n) for j in range(i) for k in range(K) } # Criar variáveis xvars = model.addVars(distK.keys(), obj=distK, vtype=GRB.BINARY, name='x') for i, j, k in xvars.keys(): xvars[j, i, k] = xvars[i, j, k] # grafo não-orientado # Restrições de grau 2, p/ cada rota k model.addConstrs( (xvars.sum(i, '*', k) == 2 for i in range(n) for k in range(K)), name='deg-2' ) # Salvar alguns atributos no modelo para acessá-los facilmente na callback model._n = n model._K = K model._xvars = xvars # Indicar limite de tempo da otimização e callback a ser chamada após a # solução ótima do modelo relaxado ser encontrada model.Params.lazyConstraints = 1 model.Params.timeLimit = 1800.0 # Restrições de disjunção entre arestas de diferentes rotas são incluídas no # modelo do problema original... if not relaxed: # Incluir restrições e otimizar model.addConstrs( (xvars.sum(i, j, '*') <= 1 for i in range(n) for j in range(i)), name='disj' ) model.optimize(subtour_elimination) # Recuperar solução x_sol = model.getAttr('x', xvars) tours = build_tours_in_sol(K, n, x_sol, xvars.keys()) # Retornar dicionário com solução ótima (ou limitantes caso o limite # de tempo seja alcançado) e tempo de execução return { 'opt': {'cost': model.objVal, 'lb': model.ObjBound, 'tours': tours}, 'runtime': model.Runtime, } # ... e dualizadas na Relaxação Lagrangiana else: # Criar variáveis para o subgradiente sgvars = model.addVars(dist.keys(), lb= - GRB.INFINITY, vtype=GRB.INTEGER, name='sg') for i, j in sgvars.keys(): sgvars[j, i] = sgvars[i, j] # grafo não-orientado # As novas variáveis são associadas às restrições dualizadas, o que # facilita na manipulação e extração desses valores model.addConstrs( ( sgvars[i,j] == - 1 + xvars.sum(i, j, '*') for i in range(n) for j in range(i) ), name='dualized' ) # Resolver método do subgradiente return subgradient(model, sgvars, dist)
def k_tsp(K, n, dist): ''' Função que define e resolve o modelo para o K-TSP, dada uma determinada instância. Aqui, K-TSP generaliza o TSP e o 2-TSP para qualquer K, o que evita a implementação de modelos diferentes. Args: K: nº de caixeiros viajantes. n: nº de vértices do grafo. dist: dicionário de custo das arestas (i,j), i >= j. Returns: Dicionário da solução, contendo as K 'tours', 'objVal' e 'runTime'. ''' # Inicializar ambiente env = gp.Env(empty=True) env.setParam('OutputFlag', 0) env.start() # Inicializar modelo model = gp.Model(name=str(K) + '-tsp', env=env) # Adaptar o dicionário de distâncias de acordo com a quantidade de # caixeiros distK = {(i, j, k): dist[i, j] for i in range(n) for j in range(i) for k in range(K)} # Criar variáveis vars = model.addVars(distK.keys(), obj=distK, vtype=GRB.BINARY, name='x') for i, j, k in vars.keys(): vars[j, i, k] = vars[i, j, k] # grafo não-orientado # Restrições de grau 2, p/ cada rota k model.addConstrs( (vars.sum(i, '*', k) == 2 for i in range(n) for k in range(K)), name='deg-2') # Restrições de disjunção entre arestas de diferentes rotas; se K = 1, tais # restrições são redundantes e eliminadas pelo solver na etapa de 'presolve' # da otimização model.addConstrs( (vars.sum(i, j, '*') <= 1 for i in range(n) for j in range(i)), name='disj') # Salvar alguns atributos no modelo para acessá-los facilmente na callback model._n = n model._K = K model._vars = vars # Otimizar modelo em no máximo 30 minutos, indicando callback a ser chamada # após a solução ótima do modelo relaxado ser encontrada model.Params.lazyConstraints = 1 model.Params.timeLimit = 1800.0 model.optimize(subtour_elimination) # Recuperar solução x_sol = model.getAttr('x', vars) edges_in_sol = gp.tuplelist( (i, j, k) for i, j, k in x_sol.keys() if x_sol[i, j, k] > 0.5) # Garantir que cada rota tenha tamanho n tours = {} for t in range(K): edges_in_tour = gp.tuplelist( (i, j) for i, j, k in edges_in_sol if k == t) tours[t] = shortest_cycle(n, edges_in_tour) assert len(tours[t]) == n # Retornar dicionário com solução return { 'tours': tours, 'objVal': model.objVal, 'runTime': model.Runtime, }
def fast_fva(lb, ub, S, c, opt_percentage=100): """A Python function to perform fva using gurobi LP solver Returns the value of the optimal solution for all the following linear programs: min/max v_i, for all coordinates i=1,...,n, subject to, Sv = 0, lb <= v <= ub Keyword arguments: lb -- lower bounds for the fluxes, i.e., a n-dimensional vector ub -- upper bounds for the fluxes, i.e., a n-dimensional vector S -- the mxn stoichiometric matrix, s.t. Sv = 0 c -- the objective function to maximize opt_percentage -- consider solutions that give you at least a certain percentage of the optimal solution (default is to consider optimal solutions only) """ if lb.size != S.shape[1] or ub.size != S.shape[1]: raise Exception( "The number of reactions must be equal to the number of given flux bounds." ) # declare the tolerance that gurobi works properly (we found it experimentally) tol = 1e-06 m = S.shape[0] n = S.shape[1] beq = np.zeros(m) A = np.zeros((2 * n, n), dtype="float") A[0:n] = np.eye(n) A[n:] -= np.eye(n, n, dtype="float") b = np.concatenate((ub, -lb), axis=0) b = np.asarray(b, dtype="float") b = np.ascontiguousarray(b, dtype="float") # call fba to obtain an optimal solution max_biomass_flux_vector, max_biomass_objective = fast_fba(lb, ub, S, c) # add an additional constraint to impose solutions with at least `opt_percentage` of the optimal solution A = np.vstack((A, -c)) b = np.append( b, -(opt_percentage / 100) * tol * math.floor(max_biomass_objective / tol)) min_fluxes = [] max_fluxes = [] try: # To avoid printint the output of the optimize() function of Gurobi, we need to set an environment like this with gp.Env(empty=True) as env: env.setParam("OutputFlag", 0) env.start() with gp.Model(env=env) as model: # Create variables x = model.addMVar( shape=n, vtype=GRB.CONTINUOUS, name="x", lb=-GRB.INFINITY, ub=GRB.INFINITY, ) # Make sparse Aeq Aeq_sparse = sp.csr_matrix(S) # Make A sparse A_sparse = sp.csr_matrix(A) # Set the b and beq vectors as numpy vectors b = np.array(b) beq = np.array(beq) # Add constraints model.addMConstr(Aeq_sparse, x, "=", beq, name="c") # Update the model to include the constraints added model.update() # Add constraints for the uneqalities of A model.addMConstr(A_sparse, x, "<", b, name="d") # Update the model with the extra constraints and then print it model.update() # Loop through the lines of the A matrix, set objective function for each and run the model for i in range(n): # Set the ith row of the A matrix as the objective function objective_function = A[i, ] # Set the objective function in the model model.setMObjective(None, objective_function, 0.0, None, None, x, GRB.MINIMIZE) model.update() # Optimize model model.optimize() # If optimized status = model.status if status == GRB.OPTIMAL: # Get the min objective value min_objective = model.getObjective().getValue() min_fluxes.append(min_objective) else: min_fluxes.append(lb[i]) # Likewise, for the maximum objective_function = np.asarray( [-x for x in objective_function]) model.setMObjective(None, objective_function, 0.0, None, None, x, GRB.MINIMIZE) model.update() model.optimize() # Again if optimized status = model.status if status == GRB.OPTIMAL: # Get the max objective value max_objective = -model.getObjective().getValue() max_fluxes.append(max_objective) else: max_fluxes.append(ub[i]) # Make lists of fluxes numpy arrays min_fluxes = np.asarray(min_fluxes) max_fluxes = np.asarray(max_fluxes) return ( min_fluxes, max_fluxes, max_biomass_flux_vector, max_biomass_objective, ) # Print error messages except gp.GurobiError as e: print("Error code " + str(e.errno) + ": " + str(e)) except AttributeError: print("Gurobi solver failed.")
def construct_Models(Hxn_nom, hxn_nom, Hxbar, hxbar, Hubar, hubar, Pinf, nx, nu, A, B, Q, R, U, N, y_0, soft_flg, Wslack, env=gp.Env()): env.setParam('OutputFlag', 0) # Initialize each as a list in case we wanted time-varying cost matrices m = [] epsilon = [] previous_u_cup = [] e_x = [] e_u = [] cost_stage = [] e_uprevious = [] U_penalty = [] U_between_penalty = [] polxnom_A = [] polxnom_b = [] for step in range(N): m.append(gp.Model(env=env, name=str(step) + "matrix")) m[step].setParam('OutputFlag', 0) # Decision variables e_x.append(m[step].addMVar(shape=(nx, N + 1 - step), lb=-GRB.INFINITY, vtype=GRB.CONTINUOUS, name=str(step) + "e_x")) e_u.append(m[step].addMVar(shape=(nu, N - step), lb=-GRB.INFINITY, vtype=GRB.CONTINUOUS, name=str(step) + "e_u")) e_uprevious.append(m[step].addMVar(shape=(nu, N), lb=-GRB.INFINITY, vtype=GRB.CONTINUOUS, name=str(step) + "e_uprevious")) previous_u_cup.append(m[step].addMVar(shape=(nu), lb=-GRB.INFINITY, vtype=GRB.CONTINUOUS, name=str(step) + "u_ball_nom")) epsilon.append(m[step].addMVar(shape=(1), vtype=GRB.CONTINUOUS, name=str(step) + "epsilon")) # Penalty for input between steps, does not change any guarantees U_penalty.append(np.diag([50, 500])) U_between_penalty.append(np.diag([50, 50])) # Initialize cost cost_stage.append(0) # k=0 # cost_stage[step] = cost_stage[step] + e_u[step][:,k]@U_penalty[step]@e_u[step][:,k]-e_u[step][:,k]@U_penalty[step]@e_uprevious[step][:,k]*2+e_uprevious[step][:,k]@U_penalty[step]@e_uprevious[step][:,k] for k in range(N - step): # Dynamics m[step].addConstr(e_x[step][:, k + 1] == A @ e_x[step][:, k] + B @ (e_u[step][:, k]), name=str(step) + "xpred" + str(k)) # State and input constraints m[step].addConstr(Hxbar @ e_x[step][:, k] <= hxbar.flatten(), name=str(step) + "Hubar1" + str(k)) m[step].addConstr(Hubar @ e_u[step][:, k] <= hubar.flatten(), name=str(step) + "Hubar2" + str(k)) # Add stage cost cost_stage[step] = cost_stage[step] + e_x[step][:, k] @ Q @ e_x[ step][:, k] + e_u[step][:, k] @ R @ e_u[step][:, k] * k**2 if k < N - 1 - step: cost_stage[step] = cost_stage[step] + e_u[ step][:, k + 1] @ U_between_penalty[step] @ e_u[ step][:, k + 1] - e_u[step][:, k + 1] @ U_between_penalty[ step] @ e_u[step][:, k] * 2 + e_u[ step][:, k] @ U_between_penalty[ step] @ e_u[step][:, k] cost_stage[step] = cost_stage[step] + e_u[step][:, k] @ U_penalty[ step] @ e_u[step][:, k] - e_u[step][:, k] @ U_penalty[ step] @ e_uprevious[step][:, k] * 2 + e_uprevious[ step][:, k] @ U_penalty[step] @ e_uprevious[step][:, k] # Terminal constraint (0 in this case) m[step].addConstr( Hxn_nom @ e_x[step][:, N - step] <= hxn_nom.flatten(), name=str(step) + "Hxnom") m[step].setObjective(cost_stage[step], GRB.MINIMIZE) m[step].update() # Add terminal cost cost_stage[step] = cost_stage[step] + e_x[ step][:, N - step] @ Pinf @ e_x[step][:, N - step] + epsilon[ step] @ epsilon[step] * soft_flg * Wslack return e_x, e_u, previous_u_cup, m, epsilon
def fast_fba(lb, ub, S, c): """A Python function to perform fba using gurobi LP solver Returns an optimal solution and its value for the following linear program: max c*v, subject to, Sv = 0, lb <= v <= ub Keyword arguments: lb -- lower bounds for the fluxes, i.e., a n-dimensional vector ub -- upper bounds for the fluxes, i.e., a n-dimensional vector S -- the mxn stoichiometric matrix, s.t. Sv = 0 c -- the linear objective function, i.e., a n-dimensional vector """ if lb.size != S.shape[1] or ub.size != S.shape[1]: raise Exception( "The number of reactions must be equal to the number of given flux bounds." ) if c.size != S.shape[1]: raise Exception( "The length of the lineart objective function must be equal to the number of reactions." ) tol = 1e-06 m = S.shape[0] n = S.shape[1] optimum_value = 0 optimum_sol = [] beq = np.zeros(m) A = np.zeros((2 * n, n), dtype="float") A[0:n] = np.eye(n) A[n:] -= np.eye(n, n, dtype="float") b = np.concatenate((ub, -lb), axis=0) b = np.asarray(b, dtype="float") b = np.ascontiguousarray(b, dtype="float") try: # To avoid printint the output of the optimize() function of Gurobi, we need to set an environment like this with gp.Env(empty=True) as env: env.setParam("OutputFlag", 0) env.start() with gp.Model(env=env) as model: # Create variables x = model.addMVar( shape=n, vtype=GRB.CONTINUOUS, name="x", lb=-GRB.INFINITY, ub=GRB.INFINITY, ) # Make sparse Aeq Aeq_sparse = sp.csr_matrix(S) # Make A sparse A_sparse = sp.csr_matrix(A) # Set the b and beq vectors as numpy vectors b = np.array(b) beq = np.array(beq) # Add constraints model.addMConstr(Aeq_sparse, x, "=", beq, name="c") # Update the model to include the constraints added model.update() # Add constraints for the uneqalities of A model.addMConstr(A_sparse, x, "<", b, name="d") # Update the model with the extra constraints and then print it model.update() objective_function = np.asarray([-x for x in c]) # Set the objective function in the model model.setMObjective(None, objective_function, 0.0, None, None, x, GRB.MINIMIZE) model.update() # Optimize model model.optimize() # If optimized status = model.status if status == GRB.OPTIMAL: optimum_value = -model.getObjective().getValue() v = model.getVars() for i in range(n): optimum_sol.append(v[i].x) optimum_sol = np.asarray(optimum_sol) return optimum_sol, optimum_value # Print error messages except gp.GurobiError as e: print("Error code " + str(e.errno) + ": " + str(e)) except AttributeError: print("Gurobi failed.")