Beispiel #1
0
def independent_set(graph, name="Independent Set"):
    """
    Generates an independent set instance according to [1].

    Parameters
    ----------
    graph: nx.Graph
        Networkx undirected graph
    name: str
        Name of the generated model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] https://www.princeton.edu/~aaa/Public/Teaching/ORF523/S16/ORF523_S16_Lec11_gh.pdf
    """
    graph = nx.relabel.convert_node_labels_to_integers(graph)
    model = scip.Model(name)

    vars = {
        str(node): model.addVar(lb=0, ub=1, obj=1, name=str(node), vtype="B")
        for node in graph.nodes
    }

    for u, v in graph.edges:
        model.addCons(vars[str(u)] + vars[str(v)] <= 1)

    model.setMaximize()

    return model
Beispiel #2
0
def set_cover(costs, sets, name="Set Cover"):
    """
    Generates basic set cover formulation.

    Parameters
    ----------
    costs: list[float]
        Cost for covering each element
    sets: list[set]
        Set constraints for elements

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    """
    model = scip.Model(name)

    # add variables and their cost
    variables = [
        model.addVar(lb=0, ub=1, obj=c, name=f"v_{i}", vtype="B")
        for i, c in enumerate(costs)
    ]

    # add constraints
    for s in sets:
        model.addCons(scip.quicksum(variables[i] for i in s) >= 1)

    model.setMinimize()

    return model
Beispiel #3
0
 def build_binary_model(self):
     self.model = scip.Model("MasterPricer")
     self.__build_variables__(tipo="B")
     self.__build_objectives__()
     self.__build_constraints__()
     self.__ensure_feasibility__()
     self.__build_convexity__()
Beispiel #4
0
def packing(n,
            m,
            costs,
            constraint_coefficients,
            limits,
            binary,
            name="Packing"):
    """Generates a packing instance as described in A.2 in [1].

    Parameters:
    ----------
    n: int
        Number of variables
    m: int
        Number of constraints
    costs: list[number] of size n
        Coefficients of objective function
    constraint_coefficients: list[list[number]] of dimensions (m x n)
        Coefficients of each variable for each constraint
    limits: list[number] of size m
        Limits of each constraint
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References:
    ----------
    .. [1] Tang, Y., Agrawal, S., & Faenza, Y. (2019). Reinforcement learning for integer
    programming: Learning to cut. arXiv preprint arXiv:1906.04859.
    """
    model = scip.Model(name)

    # add variables and their cost
    vars = []
    for i in range(n):
        cost = costs[i]
        if binary:
            var = model.addVar(lb=0, ub=1, obj=cost, name=f"v_{i}", vtype="B")
        else:
            var = model.addVar(lb=0,
                               ub=None,
                               obj=cost,
                               name=f"v_{i}",
                               vtype="I")
        vars.append(var)

    # add constraints
    for i in range(m):
        constraint_vars = (constraint_coefficients[i][j] * var
                           for j, var in enumerate(vars))
        model.addCons(scip.quicksum(constraint_vars) <= limits[i])

    # contrary to the paper (as of 05/11/2020) as a result of correspondence of with one of the authors
    model.setMaximize()

    return model
Beispiel #5
0
def triangle(graph):
    model = scip.Model("Triangle MaxCut")

    edge_variables = {}

    for u, v in itertools.combinations(graph.nodes(), 2):
        edge_name = naming.undirected_edge_name(u, v)
        if graph.has_edge(u, v):
            weight = graph.get_edge_data(u, v)["weight"]
        else:
            weight = 0
        edge_variables[edge_name] = model.addVar(lb=0,
                                                 ub=1,
                                                 obj=weight,
                                                 name=edge_name,
                                                 vtype="B")

    model.setMaximize()

    for i, j, k in itertools.combinations(graph.nodes(), 3):
        x_ij = _get_edge_variable(i, j, edge_variables)
        x_ik = _get_edge_variable(i, k, edge_variables)
        x_kj = _get_edge_variable(k, j, edge_variables)
        model.addCons(x_ij <= x_ik + x_kj)
        model.addCons(x_ij + x_ik + x_kj <= 2)

    return edge_variables, model
Beispiel #6
0
    def compute_schedule(self, laps, offset=0):
        """Compute train schedule.

            Results are dumped into a file named 'model.txt'.

            Args:
                laps (int): Number of laps that trains must run.
                offset (float): Offset.
        """

        # Initialise model
        self.model = scip.Model('Schedule')
        self.build_model(laps, offset)

        # Create objective function
        start, end = self.vars['start']['var'], self.vars['end']['var']
        objective = self.model.setObjective(end - start, sense='minimize')

        # Export file
        # self.export_model('model.txt')

        # Solve
        # self.model.hideOutput()
        self.model.optimize()

        # Obtain results
        self.get_results('solution.txt')
Beispiel #7
0
    def __init__(self, objects, stations, processing_time, **kwargs):
        # list of objects
        self.objects = objects
        # list of stations
        self.stations = stations
        # processing time dict
        self.processing_time = processing_time
        # maximal flow time
        self.max_flow_time = kwargs.get('max_flow_time', 480)
        # verbose mode
        self.verbose = kwargs.get('verbose', False)

        # init model
        self.model = pyscipopt.Model()

        # init variables
        self.start_time = self.init_start_time_var()
        self.end_time = self.init_end_time_var()
        self.delta = self.init_delta_var()

        # constraints
        self.add_last_station_cons()
        self.add_objects_order_cons()
        self.add_stations_cons()

        # objective function
        self.set_objective()

        # run optimization
        self.optimize()
Beispiel #8
0
def shuffle(model, seed, cons=True, vars=True):
    """
    Shuffles a MIP instance's rows & columns

    Parameters
    ----------
    model: scip.Model
        A pyscipopt model of the to be shuffled instance
    seed: int
        Used in shuffling (must be bigger than 0)
    cons: bool
        Whether the columns should be shuffled
    vars: bool
        Whether the rows should be shuffled

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the shuffled instance
    """
    # The following line of code does not correctly set the name! Leave commented until it's clear why.
    # shuffled = scip.Model(sourceModel=model, problemName=model.getProbName(), origcopy=True)
    assert seed > 0
    shuffled = scip.Model()
    with tempfile.NamedTemporaryFile(suffix=".mps") as temp:
        model.writeProblem(temp.name)
        shuffled.setParam("randomization/permutationseed", seed)
        shuffled.setParam("randomization/permuteconss", cons)
        shuffled.setParam("randomization/permutevars", vars)
        shuffled.readProblem(temp.name)
        shuffled.setProbName(model.getProbName())
    return shuffled
Beispiel #9
0
def assignment(graph,
               color_upperbound,
               name="Assignment Graph Coloring",
               with_variables=False):
    """
    Generates a graph coloring ILP formulation (ASS-S) as described in [1].

    Parameters
    ----------
    graph: networkx graph
        Input graph
    color_upperbound: int
        Maximum number of colors to use
    name: str
        Name of the model
    with_variables: bool
        return variables with the generated model (used to extend model)

    Returns
    -------
       model: scip.Model
        pyscipopt model of the generated instance

    References
    ----------
    .. [1] A.Jabrayilov, P.Mutzel "New Integer Linear Programming Models for the Vertex Coloring Problem"
    """
    model = scip.Model(name)

    graph = nx.convert_node_labels_to_integers(graph, first_label=0)
    colors = range(color_upperbound)

    # add variables and their cost
    x = {(vertex, color): model.addVar(lb=0,
                                       ub=1,
                                       obj=0,
                                       name=f"x_{vertex}_{color}",
                                       vtype="B")
         for vertex, color in itertools.product(graph.nodes, colors)}

    w = {
        color: model.addVar(lb=0, ub=1, obj=1, name=f"w_{color}", vtype="B")
        for color in colors
    }

    # add constraint (2)
    for v in graph.nodes:
        model.addCons(scip.quicksum(x[v, color] for color in colors) == 1)

    # add constraint (3)
    for u, v in graph.edges:
        for color in colors:
            model.addCons(x[u, color] + x[v, color] <= w[color])

    model.setMinimize()
    if with_variables:
        return model, w, x
    else:
        return model
Beispiel #10
0
def get_model():
  m = scip.Model()
  m.hideOutput()
  m.setIntParam('display/verblevel', 0)
  init_scip_params(m, seed=42)
  m.setIntParam('timing/clocktype', 2)
  m.setRealParam('limits/time', 120)
  m.setParam('limits/nodes', 1)
  return m
Beispiel #11
0
 def load_instance(self, instance_name, with_solution=False):
     if not self._instance_cached(instance_name):
         self._download_instance(instance_name)
     problem_path = self._instance_path(instance_name)
     model = scip.Model()
     model.readProblem(problem_path)
     if with_solution:
         self._add_solution(model, instance_name)
     return model
Beispiel #12
0
def scip_solve(facilities, customers, time_limit=None):
    fac = len(facilities)
    cus = len(customers)

    model = pyscipopt.Model('FL')
    model.hideOutput()
    model.setMinimize()
    #model.setRealParam('limits/gap', 0.2)

    # x_i = 1 iff facility i is chosen
    x = []  # 1xN
    # y_ij = 1 iff customer j is assigned to facility i
    y = [[] for x in range(len(facilities))]  # NxM

    for i in range(fac):
        x.append(model.addVar(name='x{}'.format(i), vtype='B'))
        for j in range(cus):
            y[i].append(model.addVar(name='y{},{}'.format(i, j), vtype='B'))

    # total demand to 1 facility <= its capacity
    for i in range(fac):
        model.addCons(
            pyscipopt.quicksum(customers[j].demand * y[i][j]
                               for j in range(cus)) <= facilities[i].capacity)

    # exactly 1 facility per customer
    for j in range(cus):
        model.addCons(pyscipopt.quicksum(y[i][j] for i in range(fac)) == 1)

    # y_ij can be 1 only if x_i is 1
    for i in range(fac):
        for j in range(cus):
            model.addCons(y[i][j] <= x[i])

    # objective
    model.setObjective(
        pyscipopt.quicksum(
            # distance facility -> customer
            length(customers[j].location, facilities[i].location) * y[i][j]
            for i in range(fac) for j in range(cus)) + pyscipopt.quicksum(
                # setup cost
                facilities[i].setup_cost * x[i] for i in range(fac)),
        'minimize')

    if time_limit is not None:
        model.setRealParam('limits/time', time_limit)
    print('SCIP starts at {}'.format(datetime.now().time()))
    model.optimize()
    val = model.getObjVal()
    assignment = []
    for j in range(cus):
        for i in range(fac):
            sol = model.getVal(y[i][j])
            if sol == 1:
                assignment.append(i)
                break
    return val, assignment
Beispiel #13
0
def representatives(graph, name="Representatives Graph Coloring"):
    """
    Generates a graph coloring ILP formulation (REP) as described in [1].

    Parameters
    ----------
    graph: networkx graph
        Input graph
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] A.Jabrayilov, P.Mutzel "New Integer Linear Programming Models for the Vertex Coloring Problem"
    """
    model = scip.Model(name)

    graph = nx.convert_node_labels_to_integers(graph, first_label=0)

    # add variables and their cost
    x = {}
    for (u, v) in itertools.product(graph.nodes, graph.nodes):
        if not graph.has_edge(u, v) or u == v:
            obj = 1 if u == v else 0
            x[u, v] = model.addVar(lb=0,
                                   ub=1,
                                   obj=obj,
                                   name=f"x_{u}_{v}",
                                   vtype="B")

    # add constraint (8)
    for v in graph.nodes:
        non_adjacent_vertices = (graph.nodes - graph.neighbors(v)).union({v})
        model.addCons(
            scip.quicksum(x[u, v] for u in non_adjacent_vertices) >= 1)

    # add constraint (9)
    for u in graph.nodes:
        non_adjacent_vertices = graph.nodes - graph.neighbors(u) - {u}
        for v, w in graph.edges:
            if v in non_adjacent_vertices and w in non_adjacent_vertices:
                model.addCons(x[u, v] + x[u, w] <= x[u, u])

    model.setMinimize()
    return model
Beispiel #14
0
def hybrid_partial_ordering(graph,
                            color_upperbound,
                            name="Hybrid Partial Ordering Graph Coloring"):
    """
    Generates a graph coloring ILP formulation (POP2) as described in [1].

    Parameters
    ----------
    graph: networkx graph
        Input graph
    color_upperbound: int
        Maximum number of colors to use
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] A.Jabrayilov, P.Mutzel "New Integer Linear Programming Models for the Vertex Coloring Problem"
    """
    model = scip.Model(name)

    graph = nx.convert_node_labels_to_integers(graph, first_label=0)
    colors = list(range(color_upperbound))

    # add variables and their cost
    x = {(vertex, color): model.addVar(lb=0,
                                       ub=1,
                                       obj=0,
                                       name=f"x_{vertex}_{color}",
                                       vtype="B")
         for vertex, color in itertools.product(graph.nodes, colors)}

    model, y, z = _partial_ordering_base_model(graph, colors, model)

    # add constraint (14)
    for v, c in itertools.product(graph.nodes, colors):
        model.addCons(x[v, c] == 1 - (y[c, v] + z[v, c]))

    # add constraint (23)
    for (u, v), c in itertools.product(graph.edges, colors):
        model.addCons(x[u, c] + x[v, c] <= 1)

    return model
Beispiel #15
0
def test_presolve():
  m = scip.Model()
  m.hideOutput()
  add_instance(m)

  m.setIntParam('display/verblevel', 0)
  init_scip_params(m, seed=42)
  m.setIntParam('timing/clocktype', 2)
  m.setRealParam('limits/time', 120)
  m.setParam('limits/nodes', 1)

  print(len(m.getConss()), len(m.getVars()))
  m.presolve()
  print(len(m.getConss()), len(m.getVars(transformed=True)))
  print(len(m.getConss()), m.getVars(transformed=True))
  m.writeProblem('/tmp/model.cip', True)
Beispiel #16
0
    def _import_problem(self):
        import pyscipopt as scip

        # Create a problem instance.
        self.int = scip.Model()

        # Import variables.
        for variable in self.ext.variables.values():
            self._import_variable(variable)

        # Import constraints.
        for constraint in self.ext.constraints:
            self._import_constraint(constraint)

        # Set objective.
        self._import_objective()
Beispiel #17
0
def test2():
  m = get_model()
  m.hideOutput()
  add_instance(m)
  m.presolve()
  orig_vars = list(
      map(lambda v: v.name.lstrip('t_'), m.getVars(transformed=True)))

  m = scip.Model(sourceModel=m, origcopy=True)
  m.hideOutput()
  m.setIntParam('display/verblevel', 0)
  init_scip_params(m, seed=42)
  m.setIntParam('timing/clocktype', 2)
  m.setRealParam('limits/time', 120)
  m.setParam('limits/nodes', 1)
  c_f, e_f, v_f = get_features_from_scip_model(m)
  assert v_f['var_names'] == orig_vars, (orig_vars, v_f['var_names'])
Beispiel #18
0
def set_packing(m, n, values, nonzero_vars_for_constraint, name="Set Packing"):
    """
    Generates a set packing formulation following [1].

    Parameters
    ----------
    m: int
        Number of constraints
    n: int
        Number of elements
    values: list[int]
        Value you get for packing each item
    nonzero_vars_for_constraint: list[list[int]]
        Nonzero variables list for each constraint
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] Yu Yang, Natashia Boland, Bistra Dilkina, Martin Savelsbergh,
    "Learning Generalized Strong Branching for Set Covering,
    Set Packing, and 0-1 Knapsack Problems", 2020.
    """
    model = scip.Model(name)

    # add variables and their cost
    vars = []
    for i in range(n):
        var = model.addVar(lb=0, ub=1, obj=values[i], name=f"v_{i}", vtype="B")
        vars.append(var)

    # add constraints
    for i in range(m):
        nonzero_vars = (vars[j] for j in nonzero_vars_for_constraint[i])
        model.addCons(scip.quicksum(nonzero_vars) <= 1)

    model.setMaximize()

    return model
Beispiel #19
0
def solve(branches):
    model = scip.Model()
    x = {}
    for m, branch in enumerate(branches):
        configurations = branch.configurations
        for n in range(len(configurations)):
            x[m, n] = model.addVar(f'x[{m}, {n}]', vtype='B')
        # Only one configuration from each branch
        model.addCons(
            scip.quicksum(x[m, n] for n in range(len(configurations))) == 1)
    model.setObjective(
        scip.quicksum(x[m, n] * branch.configurations[n].tangling
                      for m, branch in enumerate(branches)
                      for n in range(len(branch.configurations))), 'minimize')
    #model.hideOutput()
    model.optimize()

    for m, branch in enumerate(branches):
        configurations = branch.configurations
        for n in range(len(configurations)):
            print(branch.configurations[n].tangling, model.getVal(x[m, n]))
Beispiel #20
0
def naive(graph):
    model = scip.Model("Naive MaxCut")

    node_variables = {}
    for v in graph.nodes():
        node_variables[v] = model.addVar(lb=0,
                                         ub=1,
                                         obj=0,
                                         name=str(v),
                                         vtype="B")

    edge_variables = {}
    all_non_negative = True
    for u, v, d in graph.edges(data=True):
        edge_name = naming.undirected_edge_name(u, v)
        weight = d["weight"]
        edge_variables[edge_name] = model.addVar(lb=0,
                                                 ub=1,
                                                 obj=weight,
                                                 name=edge_name,
                                                 vtype="B")
        if weight < 0:
            all_non_negative = False

    model.setMaximize()

    for u, v, d in graph.edges(data=True):
        edge_name = naming.undirected_edge_name(u, v)
        model.addCons(node_variables[u] + node_variables[v] +
                      edge_variables[edge_name] <= 2)
        model.addCons(-node_variables[u] - node_variables[v] +
                      edge_variables[edge_name] <= 0)
        if not all_non_negative:
            model.addCons(node_variables[u] - node_variables[v] -
                          edge_variables[edge_name] <= 0)
            model.addCons(-node_variables[u] + node_variables[v] -
                          edge_variables[edge_name] <= 0)

    return (node_variables, edge_variables), model
Beispiel #21
0
def set_covering(graph, subsets, name="Set Covering Graph Coloring"):
    """
    Generates a graph coloring ILP formulation (COV) as described in [1].

    Parameters
    ----------
    graph: networkx graph
        Input graph
    subsets: Iterable of sets
        Independent Sets of nodes
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] A.Jabrayilov, P.Mutzel "New Integer Linear Programming Models for the Vertex Coloring Problem"
    """
    model = scip.Model(name)

    graph = nx.convert_node_labels_to_integers(graph, first_label=0)

    x = {
        tuple(s): model.addVar(lb=0, ub=1, obj=1, name=f"x_{s}", vtype="B")
        for s in subsets
    }

    for v in graph.nodes:
        model.addCons(
            scip.quicksum(x[tuple(s)] for s in subsets if v in s) >= 1)

    model.setMinimize()

    return model
Beispiel #22
0
def combinatorial_auction(bids,
                          n_dummy_items,
                          n_items,
                          name="Combinatorial Auction"):
    model = scip.Model(name)

    # add vars
    bids_per_item = [[] for item in range(n_items + n_dummy_items)]
    x = []
    for i, bid in enumerate(bids):
        bundle, price = bid
        var = model.addVar(lb=0, ub=1, obj=price, name=f"x_{i + 1}", vtype="B")
        x.append(var)
        for item in bundle:
            bids_per_item[item].append(i)

    # add constraints
    for item_bids in bids_per_item:
        if item_bids:
            vars = (x[i] for i in item_bids)
            model.addCons(scip.quicksum(vars) <= 1)
    model.setMaximize()
    return model
Beispiel #23
0
def clique_independent_set(graph, name="Clique Independent Set"):
    """
    Generates an independent set instance according to [1, 4.6.4].

    Parameters
    ----------
    graph: nx.Graph
        Networkx undirected graph
    name: str
        Name of the generated model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] David Bergman, Andre A. Cire, Willem-Jan Van Hoeve, and John Hooker. Decision diagrams
    for optimization. Springer, 2016.
    """
    graph = nx.relabel.convert_node_labels_to_integers(graph)
    model = scip.Model(name)

    cliques = _get_cliques(graph)

    vars = {
        str(node): model.addVar(lb=0, ub=1, obj=1, name=str(node), vtype="B")
        for node in graph.nodes
    }

    for clique in cliques:
        model.addCons(scip.quicksum(vars[str(node)] for node in clique) <= 1)

    model.setMaximize()

    return model
Beispiel #24
0
def knapsack(weights, profits, capacity, name="Knapsack"):
    """Generates a knapsack MIP formulation.

    Parameters:
    ----------
        profits: list[float]
            List of profits of each item
        weights: list[float]
            List of weights of each item
        capacity: float
            Capacity of knapsack

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance
    """
    assert len(weights) == len(profits)
    assert capacity >= 0
    assert all(w >= 0 for w in weights)
    assert all(p >= 0 for p in profits)

    model = scip.Model(name)
    # add variables and their cost
    variables = [
        model.addVar(lb=0, ub=1, obj=profit, vtype="B") for profit in profits
    ]
    # add constraints
    model.addCons(
        scip.quicksum(
            weight * variable
            for weight, variable in zip(weights, variables)) <= capacity)

    model.setMaximize()

    return model
Beispiel #25
0
def test_scip_features():
  m = scip.Model()
  m.hideOutput()
  add_instance(m)
  m.setIntParam('display/verblevel', 0)
  init_scip_params(m, seed=42)
  m.setIntParam('timing/clocktype', 2)
  m.setRealParam('limits/time', 120)
  m.setParam('limits/nodes', 1)

  m.presolve()
  orig_vars = list(m.getVars(transformed=True))

  l = []
  branchrule = SamplingAgent(seed=42, out=l)

  m.includeBranchrule(branchrule=branchrule,
                      name="Sampling branching rule",
                      desc="",
                      priority=666666,
                      maxdepth=1,
                      maxbounddist=1)
  m.setBoolParam('branching/vanillafullstrong/integralcands', True)
  m.setBoolParam('branching/vanillafullstrong/scoreall', True)
  m.setBoolParam('branching/vanillafullstrong/collectscores', True)
  m.setBoolParam('branching/vanillafullstrong/donotbranch', True)
  m.setBoolParam('branching/vanillafullstrong/idempotent', True)

  # m.presolve()
  m.optimize()
  # m.constructLP()

  assert len(l) == 1
  c_f, e_f, v_f = l[0]['state']
  # assert v_f['var_names'] == orig_vars, (orig_vars, v_f['var_names'])
  m.freeProb()
Beispiel #26
0
def partial_ordering(graph,
                     color_upperbound,
                     name="Partial Ordering Graph Coloring"):
    """
    Generates a graph coloring ILP formulation (POP) as described in [1].

    Parameters
    ----------
    graph: networkx graph
        Input graph
    color_upperbound: int
        Maximum number of colors to use
    name: str
        Name of the model

    Returns
    -------
    model: scip.Model
        A pyscipopt model of the generated instance

    References
    ----------
    .. [1] A.Jabrayilov, P.Mutzel "New Integer Linear Programming Models for the Vertex Coloring Problem"
    """
    model = scip.Model(name)

    graph = nx.convert_node_labels_to_integers(graph, first_label=0)
    colors = list(range(color_upperbound))

    model, y, z = _partial_ordering_base_model(graph, colors, model)

    # add constraint (20)
    for (u, v), c in itertools.product(graph.edges, colors[:-1]):
        model.addCons(y[c, u] + z[u, c] + y[c, v] + z[v, c] >= 1)

    return model
Beispiel #27
0
    def staff(self):
        """
        Produce the staffing /workforce schedule.
        Minimize workers needed, given the constraints
        Note that the problem can be essentially decompose by area as we assume that workers don't cross areas in one day (and possibly the entire week?).

        Step 1 [DONE]:
          * Routes given, just assign the number of workers depending on how much waste we expect?
          * In fact, one could consider each area to be a route and it will give a number of people assigned to an area, assuming they can then determine and divide the routes among themselves.
          * Alternatively, could determine the routes by just clustering toilets, putting together toilets so that we just about fill up Worker's carry limit per route
          * Constraints on waste lifted/carried (assume the crew leader can lift and then each of the workers)
          * Use one worker at most 5 days a week?

         z_crd: collector c assigned to route r available on day d. A limited number of workers?
         gamma_td: toilet t should be collected on day d
         gamma_rd: route r should be collected on day d. gamma_rd = max_t gamma_td * chi_rt. If a route coincides with an area, then can have gamma_rd = 1 (assuming every area has some toilets to be collected?)
         lambda_rd: Collector lower bound per route... normally 2, 3 for handcarts
         chi_rt: toilet t lies on route r
         w_rd: weight (predicted) on route r on day d. w_rd = sum_t chi_rt * w_td
         W: weight limit per worker per day

         min_{z_crd} sum z_crd
         sum_rd z_crd <= 5 forall c
         sum_c z_crd >= 2*gamma_rd forall r,d #Assign 2+ workers if a route is to be collected. Needs to have at least 2, we know... for the wheelbarrow routes, need to have at least three?
         sum_c z_crd * W >= w_rd forall r, d

        Step 2:
          * Upper bound on workers and a penalty for uncollectable toilets (flexible workforce vs toilet overflows)
          * Extra constraints on worker unavailability

        Step 3:
        #TODO
          * "Routing" within areas. Consider just aerial distances between toilets. The thing is, we could consolidate some routes when we only collect occasionally.
          * Collection window constraints (because the waste accumulates when?)
          * Constraints on the distance traveled by a worker

        Step 4: Other constraints/objectives
          * Training (have workers rotate through different areas)
        """
        if self.schedule is None or self.waste is None:
            self.log.warning(
                "The staffing routine has not received scheduling or waste prediction data. The workforce schedule will not be created."
            )
            return ([None, None, None])

        self.preprocess()

        s = scip.Model("Staffing")
        s.hideOutput()
        s.setMinimize()

        #Vars: z_crd: collector c assigned to route r for day d
        assign_vars = {}
        for c in range(0, self.parameters['N']):
            for i_r, r in enumerate(self.routes):
                for d in self.next_days:
                    v_name = 'z' + str(c) + str(i_r) + str(d)
                    #This also handles the objective function...
                    assign_vars[c, r, d] = s.addVar(v_name, vtype='B', obj=1.0)

        #Constraints
        for c in range(0, self.parameters['N']):
            #1) Collector workday limits
            w_name = 'Worker_' + str(c)
            coeffs_worker = {
                assign_vars[(c, r, d)]: 1
                for d in self.next_days for i_r, r in enumerate(self.routes)
            }
            s.addCons(coeffs=coeffs_worker,
                      rhs=self.parameters['D'],
                      name=w_name)
        for d in self.next_days:
            for i_r, r in enumerate(self.routes):
                #2) Collector weight limits on routes. For each route: assign 2+ workers and also more than the predicted weight per the route
                #3) Assign n+ workers on the route
                route_weight_name = 'Route_Weight_' + str(i_r) + "_" + str(d)
                route_collector_minimum_name = 'Route_Minimum_' + str(
                    i_r) + "_" + str(d)
                coeffs_weight = {
                    assign_vars[(c, r, d)]: 1
                    for c in range(0, self.parameters['N'])
                }  #sum_c z_crd >= w_rd / W
                coeffs_collectors = {
                    assign_vars[(c, r, d)]: 1
                    for c in range(0, self.parameters['N'])
                }

                weight_limit = self.is_the_route_collected_on_day[
                    d, r] * self.route_waste[d, r] / self.parameters['W']
                collect_limit = self.is_the_route_collected_on_day[
                    d, r] * self.parameters['NR']
                s.addCons(coeffs=coeffs_weight,
                          lhs=weight_limit,
                          rhs=None,
                          name=route_weight_name)
                s.addCons(coeffs=coeffs_collectors,
                          lhs=collect_limit,
                          rhs=None,
                          name=route_collector_minimum_name)

        #Write down the formulation to make sure it was formulated correctly.
        #s.writeProblem()
        #Objective function
        #Done: See in Vars
        s.optimize()
        vars = s.getVars()
        #for v in vars[0:50]:
        #    print(s.getVal(v))

        roster = self.createRoster(s, assign_vars)
        self.log.debug(s.printStatistics())
        return (roster, s, assign_vars)
Beispiel #28
0
    # Dict {method_name:[list_of_primal_integral]}
    # agg_integral_stats = {'internal':[],'MCTS_vanilla':[], 'MCTS_hot_start':[]}
    agg_integral_stats = {x['type']: [] for x in branching_policies}

    # Same, but each containing the raw info (nb lp solves and feas solutions)
    raw_stats = {
        'internal': [],
        'MCTS_coef_diving': [],
        'MCTS_coef_diving': []
    }
    for instance in instances:
        for policy in branching_policies:
            brancher = PolicyBranching(policy)

            # Model will be initialized
            m = scip.Model()

            # print(a)

            m.setIntParam('display/verblevel', 0)

            m.readProblem(f"{instance['path']}")

            m.includeEventhdlr(SolvingStatsRecorder(brancher.solving_stats),
                               "SolvingStatsRecorder", "")

            # AGGRESSIVE MODE FOR INTERNAL BRANCHING
            if policy['type'] == 'internal':
                utilities.init_scip_params(m,
                                           seed=seed,
                                           heuristics='agg',
    def run_episode(self,
                    instance,
                    name,
                    policy,
                    scip_seed,
                    cutoff_value,
                    scip_limits,
                    scip_params,
                    verbose,
                    brancher_name='SCIPEvalBrancher'):
        """
        :param instance: str, pathway to instance.mps.gz
        :param name: str, name of the instance (w/o extension)
        :param policy: str, SCIP branching rule to be used
        :param scip_seed: int, SCIP solver seed
        :param cutoff_value: float, cutoff
        :param scip_limits: dict, specifying SCIP parameter limits
        :param scip_params: dict, specifying SCIP parameter setting
        :param verbose: bool, verbosity
        :param brancher_name: str, name of the brancher to be defined
        :return:
            exp_dict: dict, containing basic statistics on the experiment (run)
        """
        print("\nRunning SCIP evaluation on instance {}".format(name))
        m = scip.Model()

        # set static solver setting (scip seed and cutoff are set below)
        utilities.init_params(m, scip_limits, scip_params)

        # set scip parameters as needed (wrt the current episode setting)
        m.setBoolParam('randomization/permutevars', True)
        m.setIntParam('randomization/permutationseed',
                      scip_seed)  # SCIP default at 0

        m.readProblem(instance)

        if scip_params['cutoff']:
            assert cutoff_value is not None
            m.setObjlimit(cutoff_value)

        brancher = SCIPEvalBrancher(model=m, policy=policy, verbose=verbose)
        m.includeBranchrule(brancher,
                            name=brancher_name,
                            desc="bla",
                            priority=999999,
                            maxdepth=-1,
                            maxbounddist=1)

        # optimize, i.e., perform the solve
        t0 = time.time()
        t0_process = time.process_time()
        m.optimize()
        t1_process = time.process_time()
        t1 = time.time()

        print(
            "\tInstance {}. SCIP time: {} (wall-clock: {}). Nnodes: {}. FairNNodes: {}"
            .format(name, m.getSolvingTime(), t1 - t0, m.getNNodes(),
                    m.getFairNNodes(bytes(brancher_name, 'utf-8'))))

        # store episode_data
        exp_dict = {
            'name': name,
            'policy': policy,
            'seed': scip_seed,
            'nnodes': m.getNNodes(),
            'fair_nnodes':
            m.getFairNNodes(bytes(brancher_name,
                                  'utf-8')),  # needs bytes encoding
            'nnodes_left': m.getNNodesLeft(),
            'nLP_iterations': m.getNLPIterations(),
            'max_depth': m.getMaxDepth(),
            'status': m.getStatus(),
            'gap': m.getGap(),
            'primal_bound': m.getPrimalbound(),
            'dual_bound': m.getDualbound(),
            'primaldualintegral': m.getPrimalDualIntegral(),
            'scip_solve_time': m.getSolvingTime(),
            'scip_presolve_time': m.getPresolvingTime(),
            'opt_time_process': t1_process - t0_process,
            'opt_time_wallclock': t1 - t0,
            'nnodes_list': brancher.nnodes_list,
            'nnodesleft_list': brancher.nnodesleft_list,
        }

        m.freeProb()

        return exp_dict
Beispiel #30
0
 def layout_paths(self):
     m = scip.Model()