Beispiel #1
1
def solve_tsp(V,c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """

    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(quicksum(x[i,j] for i in S for j in S if j>i) <= len(S)-1)
            print("cut: len(%s) <= %s" % (S,len(S)-1))
        return True


    def addcut2(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))

        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            T = set(V) - set(S)
            print("S:",S)
            print("T:",T)
            model.addCons(quicksum(x[i,j] for i in S for j in T if j>i) +
                          quicksum(x[i,j] for i in T for j in S if j>i) >= 2)
            print("cut: %s >= 2" % "+".join([("x[%s,%s]" % (i,j)) for i in S for j in T if j>i]))
        return True

    # main part of the solution process:
    model = Model("tsp")

    model.hideOutput() # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i,j] = model.addVar(ub=1, name="x(%s,%s)"%(i,j))

    for i in V:
        model.addCons(quicksum(x[j,i] for j in V if j < i) + \
                        quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    model.setObjective(quicksum(c[i,j]*x[i,j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i,j) in x:
            if model.getVal(x[i,j]) > EPS:
                edges.append( (i,j) )

        if addcut(edges) == False:
            if isMIP:     # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,j) in x:     # all components connected, switch to integer model
                model.chgVarType(x[i,j], "B")
                isMIP = True

    return model.getObjVal(),edges
Beispiel #2
0
def encode_scipplan(domain, instance, horizon, epsilon, gap):

    bigM = 1000.0
    
    model = Model(domain + "_" + instance + "_" + str(horizon))
    
    initials = readConstraintFiles("./translation/initial_" + domain + "_" + instance+".txt")
    instantaneous_constraints = readConstraintFiles("./translation/instantaneous_constraints_" + domain + "_" + instance+".txt")
    temporal_constraints = readConstraintFiles("./translation/temporal_constraints_" + domain + "_" + instance+".txt")
    goals = readConstraintFiles("./translation/goal_" + domain + "_" + instance+".txt")
    transitions = readTransitions("./translation/transitions_" + domain + "_" + instance+".txt")
    reward = readReward("./translation/reward_" + domain + "_" + instance+".txt")
    
    A, S, Aux, A_type, S_type, Aux_type = readVariables("./translation/pvariables_"+domain+"_"+instance+".txt")
    
    model, x, y, v, d = initialize_original_variables(model, A, S, Aux, A_type, S_type, Aux_type, horizon)

    model = encode_initial_constraints(model, S, y, initials)
    model, d, v, Aux = encode_global_instantaneous_constraints(model, A, S, Aux, x, y, v, d, instantaneous_constraints, horizon, bigM)
    model, d, v, Aux = encode_global_temporal_constraints(model, A, S, Aux, x, y, v, d, temporal_constraints, horizon, bigM)
    model, d = encode_transitions(model, A, S, Aux, x, y, v, d, transitions, horizon)
    model, d = encode_goal_constraints(model, S, Aux, y, v, d, goals, horizon)
    model = encode_reward(model, A, S, Aux, x, y, v, d, reward, horizon)
    
    for t in range(horizon):
        model.addCons(x[("dt",t)] <= bigM)
        
    model.setRealParam("limits/gap", gap)
    
    while True:
        model.optimize()
        if len(model.getSols()) == 0:
            print("Problem is infeasible for the given horizon.")
            return False
        violated_t, interval, violated_c_index = checkTemporalConstraintViolation(model, A, S, Aux, x, y, v, d, temporal_constraints, horizon, epsilon)
        #print(violated_t, interval, violated_c_index)
        if violated_t == -1:
            break
        zero_crossing_coef = ((interval[1] + interval[0]) / 2.0) / model.getVal(x[("dt",violated_t)])
        model.freeTransform()
        model = encode_violated_global_temporal_constraint(model, A, S, Aux, x, y, v, d, temporal_constraints, horizon, violated_t, zero_crossing_coef, violated_c_index)
    
    print("Plan:")
    for t in range(horizon):
        for index, a in enumerate(A):
            print(a + " at time " + str(t) + " by value " + str(model.getVal(x[(a,t)])))
    return True
Beispiel #3
0
def test_solution_getbest():
    m = Model()

    x = m.addVar("x", lb=0, ub=2, obj=-1)
    y = m.addVar("y", lb=0, ub=4, obj=0)
    m.addCons(x * x <= y)

    m.optimize()

    sol = m.getBestSol()
    assert round(sol[x]) == 2.0
    assert round(sol[y]) == 4.0
    print(sol)  # prints the solution in the transformed space

    m.freeTransform()
    sol = m.getBestSol()
    assert round(sol[x]) == 2.0
    assert round(sol[y]) == 4.0
    print(sol)  # prints the solution in the original space
Beispiel #4
0
def scip_solver(graph, weight='weight'):
    try:
        from pyscipopt import Model, quicksum
    except ImportError:
        raise ImportError(
            'SCIP Optimization Suit with Python support not found')
    nodes = graph.nodes()
    num_nodes = graph.number_of_nodes()
    c = nx.adjacency_matrix(graph, weight=weight)
    # Define the optimization problem
    model = Model('tsp')
    model.hideOutput()  # silent/verbose mode
    # Create the variables
    x = {}
    for i in xrange(num_nodes):
        for j in xrange(i + 1, num_nodes):
            x[i, j] = model.addVar(ub=1, name='x(%s,%s)' % (i, j))
    # Add the constraints
    for i in xrange(num_nodes):
        model.addCons(
            quicksum([x[j, i] for j in xrange(i)]) +
            quicksum([x[i, j] for j in xrange(i + 1, num_nodes)]) == 2,
            'Degree(%s)' % i)
    # Set minimization objective
    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in xrange(num_nodes)
                 for j in xrange(i + 1, num_nodes)), 'minimize')

    # Limit the number of edges in a connected component S to |S|-1
    def addcut(cut_edges):
        G = nx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(nx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
        return True

    # Solve
    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))
        if addcut(edges) == False:
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], 'B')
                isMIP = True
    # Extract the tour from the edges
    G = nx.Graph()
    for e in edges:
        G.add_edge(nodes[e[0]], nodes[e[1]])
    tour_edges = nx.eulerian_circuit(G, source=graph.nodes_iter().next())
    tour = [e[0] for e in tour_edges]
    return tour
def tsp_solver(c, customers, vehicle_tours):
    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S) <= len(S) - 1)

        return True

    # Add the depot on each vehicle
    vehicle_tours = {k: vehicle_tours[k] + [0] for k in vehicle_tours.keys()}
    final_obj = 0
    final_tours = []
    for key, value in vehicle_tours.iteritems():
        v_customers = value
        model = Model("vrp_tsp")
        #model.hideOutput()
        x = {}

        for i in v_customers:
            for j in v_customers:
                # vehicle moves from customer i to customer j
                x[i, j] = model.addVar(vtype="B", name="x(%s,%s)" % (i, j))

        for i in v_customers:
            # Constraint: every customer can only be visited once
            # (or, every node must be connected and connect to another node)
            model.addCons(quicksum(x[i, j] for j in v_customers) == 1)
            model.addCons(quicksum(x[j, i] for j in v_customers) == 1)

            for j in v_customers:
                if i == j:
                    # Constraint: a node cannot conect to itself
                    model.addCons(x[i, j] == 0)

        # Objective function: minimize total distance of the tour
        model.setObjective(
            quicksum(x[i, j] * c[(i, j)] for i in v_customers
                     for j in v_customers), "minimize")

        EPS = 1.e-6
        isMIP = False
        while True:
            model.optimize()
            edges = []
            for (i, j) in x:
                if model.getVal(x[i, j]) > EPS:
                    edges.append((i, j))

            if addcut(edges) == False:
                if isMIP:  # integer variables, components connected: solution found
                    break
                model.freeTransform()
                for (
                        i, j
                ) in x:  # all components connected, switch to integer model
                    model.chgVarType(x[i, j], "B")
                    isMIP = True

        # model.optimize()
        best_sol = model.getBestSol()
        sub_tour = []

        # Build the graph path
        # Retrieve the last node of the graph, i.e., the last one connecting to the depot
        last_node = [n for n in edges if n[1] == 0][0][0]
        G = networkx.Graph()
        G.add_edges_from(edges)
        path = list(networkx.all_simple_paths(G, source=0, target=last_node))
        path.sort(reverse=True, key=lambda u: len(u))

        if len(path) > 0:
            path = path[0][1:]
        else:
            path = path[1:]

        obj = model.getSolObjVal(best_sol)
        final_obj += obj
        final_tours.append([customers[i] for i in path])

        # print("Customers visited by vehicle %s: %s" % (key, value))
        # print("Objective cost for vehicle %s: %s" % (key, obj))
        # print("Edges visited by vehicle %s: %s" % (key, edges))
        # print("Path visited by vehicle %s: %s" % (key, path))
    return final_obj, final_tours
def scip_solver_2(customers, customer_count, vehicle_count, vehicle_capacity):
    model = Model("vrp")

    c_range = range(0, customer_count)
    cd_range = range(1, customer_count)

    x, d, w, v = {}, {}, {}, {}
    for i in c_range:
        for j in c_range:
            d[i, j] = length(customers[i], customers[j])
            w[i, j] = customers[i].demand + customers[j].demand
            if j > i and i == 0:  # depot
                x[i, j] = model.addVar(ub=2,
                                       vtype="I",
                                       name="x(%s,%s)" % (i, j))
            elif j > i:
                x[i, j] = model.addVar(ub=1,
                                       vtype="I",
                                       name="x(%s,%s)" % (i, j))

    model.addCons(
        quicksum(x[0, j] for j in cd_range) <= 2 * vehicle_count,
        "DegreeDepot")

    for i in cd_range:
        model.addCons(
            quicksum(x[j, i] for j in c_range if j < i) +
            quicksum(x[i, j] for j in c_range if j > i) == 2, "Degree(%s)" % i)

        # model.addCons(quicksum(x[j, i] * w[j, i] for j in c_range if j < i) +
        #              quicksum(x[i, j] * w[i, j] for j in c_range if j > i) <= 2*vehicle_capacity)

        # for j in cd_range:
        #    for z in cd_range:
        #        if j > i and z > j:
        #            x[i, j] + x[j, z] + x[i, z] <= 2

        # for j in c_range:
        #    if j > i:
        #        model.addCons(x[i, j] * w[i, j] <= vehicle_capacity)

    model.setObjective(
        quicksum(d[i, j] * x[i, j] for i in c_range for j in c_range if j > i),
        "minimize")

    # model.hideOutput()

    # mip_gaps = [0.9, 0.5, 0.2, 0.03]
    mip_gaps = [0.0]
    runs = 0

    start = datetime.now()
    for gap in mip_gaps:
        model.freeTransform()
        model.setRealParam("limits/gap", gap)
        # model.setRealParam("limits/absgap", 0.3)
        model.setRealParam("limits/time", 60 * 20)  # Time limit in seconds
        # model.setIntParam('limits/bestsol', 1)

        edges, final_edges, runs = optimize(customer_count, customers, model,
                                            vehicle_capacity, vehicle_count, x)

    # model.setIntParam('limits/bestsol', -1)
    # model.freeTransform()
    # model.setRealParam("limits/gap", 0)
    # edges, final_edges = optimize(customer_count, customers, model, vehicle_capacity, vehicle_count, x)

    run_time = datetime.now() - start

    print edges
    print final_edges
    output = [[]] * vehicle_count
    for i in range(vehicle_count):
        output[i] = []
        current_item = None
        if len(final_edges) > 0:
            # Get the first edge starting with 0
            for e in final_edges:
                if e[0] == 0:
                    current_item = e
                    break
            if current_item:
                a = current_item[0]
                current_node = current_item[1]
                output[i].append(customers[current_node])
                final_edges.remove(current_item)
                searching_connections = True
                while searching_connections and len(final_edges) > 0:
                    for edge in final_edges:
                        found_connection = False
                        a_edge = edge[0]
                        b_edge = edge[1]

                        # If we find the node connecting with a 0
                        # it means the cycle has been closed
                        if b_edge == current_node and a_edge == 0:
                            final_edges.remove(edge)
                            break

                        if a_edge == current_node:
                            output[i].append(customers[b_edge])
                            current_node = b_edge
                            found_connection = True
                        elif b_edge == current_node:
                            output[i].append(customers[a_edge])
                            current_node = a_edge
                            found_connection = True

                        if found_connection:
                            final_edges.remove(edge)
                            break

                    if not found_connection:
                        searching_connections = False

    print output

    sol = model.getBestSol()
    obj = model.getSolObjVal(sol)

    print("RUN TIME: %s" % str(run_time))
    print("NUMBER OF OPTIMIZATION RUNS: %s" % runs)

    return obj, output
Beispiel #7
0
def tsp2lp(V, c, filename):
    def addcut(cut_edges):

        # Initialize graph
        G = nx.Graph()
        G.add_edges_from(cut_edges)

        if nx.number_connected_components(G) == 1:
            return False

        model.freeTransform()

        for S in nx.connected_components(G):
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            # print("cut: len(%s) <= %s" % (S, len(S) - 1))
        return True

    def addcut2(cut_edges):

        # Initialize graph
        G = nx.Graph()
        G.add_edges_from(cut_edges)

        if nx.number_connected_components(G) == 1:
            return False

        model.freeTransform()

        for S in nx.connected_components(G):
            T = set(V) - set(S)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)
        return True

    model = Model()
    model.hideOutput()  # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(
            quicksum(x[j, i]
                     for j in V if j < i) + quicksum(x[i, j]
                                                     for j in V if j > i) == 2,
            "Degree(%s)" % i,
        )

    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))

        if addcut(edges) == False:
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")
                isMIP = True

    model.writeProblem(filename)

    return model
Beispiel #8
0
def solve_tsp(V, c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """
    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = networkx.connected_components(G)
        if len(Components) == 1:
            return False
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            print("cut: len(%s) <= %s" % (S, len(S) - 1))
        return True

    def addcut2(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = networkx.connected_components(G)

        if len(Components) == 1:
            return False
        for S in Components:
            T = set(V) - set(S)
            print("S:", S)
            print("T:", T)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)
            print("cut: %s <--> %s >= 2" % (S, T),
                  [(i, j) for i in S for j in T if j > i])
        return True

    def isMIP(x):
        for var in x:
            if var.vtype == "CONTINUOUS":
                return False
        return True

    # main part of the solution process:
    model = Model("tsp")

    # model.Params.OutputFlag = 0 # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(quicksum(x[j,i] for j in V if j < i) + \
                        quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))

        model.freeTransform()
        if addcut(edges) == False:
            if isMIP(
            ):  # integer variables, components connected: solution found
                break
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")

    return model.getObjVal(), edges
Beispiel #9
0
def solve_tsp(V, c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """
    def addcut(cut_edges, fn_constrain):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        print('len(components): %s' % len(Components))
        if len(Components) == 1:
            return False
        model.freeTransform()
        fn_constrain(Components)
        return True

    def subtour_elim(Components):
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            # print("cut: len(%s) <= %s" % (S, len(S) - 1))

    def cutset(Components):
        for S in Components:
            T = set(V) - set(S)
            # print("S:", S)
            # print("T:", T)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)


#             print("cut: %s >= 2" % "+".join(
#                 [("x[%s,%s]" % (i, j)) for i in S for j in T if j > i]))

# main part of the solution process:

    model = Model("tsp")
    model.hideOutput()  # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:  # for symmetric graph, only consider direction: j > i
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(
            quicksum(x[j, i]
                     for j in V if j < i) + quicksum(x[i, j]
                                                     for j in V if j > i) == 2,
            "Degree(%s)" % i)
    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))
        if not addcut(edges, subtour_elim):
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            print('chgVarType to Integer')
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")
                isMIP = True

    return model.getObjVal(), edges
Beispiel #10
0
def scip_solver_2(customers, customer_count, vehicle_count, vehicle_capacity):
    model = Model("vrp")

    c_range = range(0, customer_count)
    cd_range = range(1, customer_count)

    x, d, w, v = {}, {}, {}, {}
    for i in c_range:
        for j in c_range:
            d[i, j] = length(customers[i], customers[j])
            w[i, j] = customers[i].demand + customers[j].demand
            if j > i and i == 0:  # depot
                x[i, j] = model.addVar(ub=2,
                                       vtype="I",
                                       name="x(%s,%s)" % (i, j))
            elif j > i:
                x[i, j] = model.addVar(ub=1,
                                       vtype="I",
                                       name="x(%s,%s)" % (i, j))

    model.addCons(
        quicksum(x[0, j] for j in cd_range) <= 2 * vehicle_count,
        "DegreeDepot")

    for i in cd_range:
        model.addCons(
            quicksum(x[j, i] for j in c_range if j < i) +
            quicksum(x[i, j] for j in c_range if j > i) == 2, "Degree(%s)" % i)

    model.setObjective(
        quicksum(d[i, j] * x[i, j] for i in c_range for j in c_range if j > i),
        "minimize")

    mip_gaps = [0.0]
    runs = 0

    start = datetime.now()
    for gap in mip_gaps:
        model.freeTransform()
        model.setRealParam("limits/gap", gap)
        model.setRealParam("limits/time", 60 * 20)  # Time limit in seconds
        edges, final_edges, runs = optimize(customer_count, customers, model,
                                            vehicle_capacity, vehicle_count, x)

    run_time = datetime.now() - start

    print(edges)
    print(final_edges)
    output = [[]] * vehicle_count
    for i in range(vehicle_count):
        output[i] = []
        current_item = None
        if len(final_edges) > 0:

            for e in final_edges:
                if e[0] == 0:
                    current_item = e
                    break
            if current_item:
                a = current_item[0]
                current_node = current_item[1]
                output[i].append(customers[current_node])
                final_edges.remove(current_item)
                searching_connections = True
                while searching_connections and len(final_edges) > 0:
                    for edge in final_edges:
                        found_connection = False
                        a_edge = edge[0]
                        b_edge = edge[1]

                        if b_edge == current_node and a_edge == 0:
                            final_edges.remove(edge)
                            break

                        if a_edge == current_node:
                            output[i].append(customers[b_edge])
                            current_node = b_edge
                            found_connection = True
                        elif b_edge == current_node:
                            output[i].append(customers[a_edge])
                            current_node = a_edge
                            found_connection = True

                        if found_connection:
                            final_edges.remove(edge)
                            break

                    if not found_connection:
                        searching_connections = False

    print(output)

    sol = model.getBestSol()
    obj = model.getSolObjVal(sol)

    print("RUN TIME: %s" % str(run_time))
    print("NUMBER OF OPTIMIZATION RUNS: %s" % runs)

    return obj, output
def solve(event):
	global n, trucks, solver, li, ax, warehouses, loc_x, loc_y, lines_x, lines_y, obj_value, exec_value
	global distances, distances2
	print("solving...")

	ax.clear()
	ax.set_xlim(0,1000)
	ax.set_ylim(0,1000)
	plot_data(ax, loc_x, loc_y, warehouses)
	ax.text(400, 1075, "solving...", family="serif", horizontalalignment='left', verticalalignment='top')
	plt.draw()
	fig.canvas.draw()

	start_time = time.time()

	# Calculate constraint for Li
	total_l = 0
	total_c = 0.0
	paths = 0
	slack = False
	while total_c < n or paths < trucks:
		c1 = min(c,n)
		total_c += c1
		total_l += c1*(c1+1)/2
		paths += 1

	if total_c > n:
		slack = True

	if solver=="cbc" or solver=="glpk" or solver=="gurobi":
		# Objective function
		z = pulp.LpProblem('Test', pulp.LpMinimize)

		# Generate decision variables
		x = {}
		y = {}
		variables = []
		l = {}
		s = {}
		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if i==j:
					continue
				x[i,j] = pulp.LpVariable('x_' + str(i) + '_' + str(j), 0, 1, pulp.LpInteger)
			if i >= warehouses:
				l[i] = pulp.LpVariable('l_' + str(i), 1, min(n,c), pulp.LpInteger)

		# Objective function
		z += pulp.lpSum([distances[i][j] * x[i,j] for i in range(n+warehouses) for j in 
			list(range(i)) + list(range(i+1,n+warehouses))])

		# Constraints
		constraintSeq = []
		constraintTrucks = []
		for i in range(n+warehouses):
			if i>=warehouses:
				constraintSeq.append(l[i])
			constraintFrom = []

			constraintTo = []
			for j in range(n+warehouses):
				if i==j:
					continue
				if i>=warehouses and j>=warehouses:
					z += pulp.lpSum([l[i], -1*l[j], n*x[i,j], -n+1]) <= 0

				if i>=warehouses:
					constraintFrom.append(x[i,j])
					constraintTo.append(x[j,i])
				if i<warehouses:
					constraintTrucks.append(x[j,i])
				if i>=warehouses:
					z += pulp.lpSum(constraintFrom) == 1 # paths from location
					z += pulp.lpSum(constraintTo) == 1 # paths to location
				if i==warehouses and (paths > 1 or warehouses>1):
					z += pulp.lpSum(constraintTrucks) == paths # paths to warehouse

		if not slack:
		z += pulp.lpSum(constraintSeq) == total_l
		else:
		z += pulp.lpSum(constraintSeq) <= total_l

		# Solve
		if solver=="cbc":
			status = z.solve()
		if solver=="glpk":
			status = z.solve(pulp.GLPK())		
		if solver=="gurobi":
			status = z.solve(pulp.GUROBI_CMD())

		# should be 'Optimal'
		if pulp.LpStatus[status]!="Optimal":
			print("RESULT: ".pulp.LpStatus[status])
		print("Objective function value: "+str(z.objective.value()))

		# Print variables & save path
		lines_x = []
		lines_y = []
		li = [0] * n
		for i in range(n+warehouses):
			if i>=warehouses:
				li[i-warehouses] = pulp.value(l[i])
			for j in range(n+warehouses):
				if i==j:
					continue
				if pulp.value(x[i,j]) == 1:
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)

		obj_value = "c=" + str(round(z.objective.value(),2))

	elif solver=="cut plane":
		model = Model("tsp")
		model.hideOutput()
		x = {}
		l = {}
		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if i != j:
					x[i,j] = model.addVar(ub=1, name="x(%s,%s)"%(i,j))
			if (paths > 1 or warehouses > 1) and i >= warehouses:
				l[i] = model.addVar(ub=min(c,n),lb=1, name="l(%s)"%(i))

		if paths == 1 and warehouses == 1:

			# SYMMETRIC DISTANCE MATRIX ONLY
			#for i in range(n+warehouses):
			#model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) + \
			# quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 2,"Degree(%s)"%i)

			# ASYMMETRIC DISTANCE MATRIX
			for i in range(n+warehouses):
				model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) == 1,"In(%s)"%i)
				model.addCons(quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 1,"Out(%s)"%i)
		
		else:
			for i in range(warehouses, n+warehouses):
				model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) == 1,"In(%s)"%i)
				model.addCons(quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 1,"Out(%s)"%i)

			for i in range(warehouses, n+warehouses):
				for j in range(warehouses, n+warehouses):
					if i!=j:
						model.addCons(l[i] -l[j] +n*x[i,j] <= n-1, "Li(%s,%s)"%(i,j))
			model.addCons(quicksum(x[j,i] for i in range(warehouses) for j in range(n+warehouses) if i!=j) == paths, "Paths(%s)"%paths)

			if not slack:
				model.addCons(quicksum(l[i] for i in range(warehouses,n+warehouses)) == total_l,"TotalL")
			
			else:
				model.addCons(quicksum(l[i] for i in range(warehouses,n+warehouses)) <= total_l, "TotalL")
		
		model.setObjective(quicksum(distances2[i,j]*x[i,j] for (i,j) in x), "minimize")

		EPS = 1.e-6
		isMIP = False
		model.setPresolve(SCIP_PARAMSETTING.OFF)

		while True:
			model.optimize()
			#edges = []
			lines_x = []
			lines_y = []
			edges = []
			li = [0] * n
			for (i,j) in x:
				# i=j already skipped
				if model.getVal(x[i,j]) > EPS:
					#edges.append( (i,j) )
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)
					edges.append( (i,j) )
			if paths>1 or warehouses>1:
				for i in range(warehouses, n+warehouses):
					li[i-warehouses] = int(model.getVal(l[i]))

			obj_value = "c=" + str(round(model.getObjVal(),2))

			ax.clear()
			ax.set_xlim(0,1000)
			ax.set_ylim(0,1000)

			plot_data_lines(lines_x,lines_y)
			plot_data(ax, loc_x, loc_y, warehouses)
			ax.text(400, 1075, "solving...", family="serif", horizontalalignment='left', verticalalignment='top')

			fig.canvas.draw()

			if addcut(edges,model,x,warehouses) == False:
				if isMIP: # integer variables, components connected: solution found
					break
				model.freeTransform()
				for (i,j) in x: # all components connected, switch to integer model
					model.chgVarType(x[i,j], "B")
				if paths > 1 or warehouses > 1:
					for i in range(warehouses,n+warehouses):
						model.chgVarType(l[i], "I")
				isMIP = True

		sol_li = [0] * (n+warehouses)
		sol_xij = {}

		print('solved.')
	elif solver == 'held-karp':
		li = [0] * n
		opt, path = held_karp(distances)
		print(path)
		obj_value = "c=" + str(round(opt,2))
		x = [[0 for x in range(n+warehouses)] for y in range(n+warehouses)]
		for idx, val in enumerate(path):
			if idx < (len(path)-1):
				x[val][path[idx+1]] = 1;
			elif idx == (len(path)-1):
				x[val][path[0]] = 1;

		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if x[i][j] == 1:
					#edges.append( (i,j) )
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)
					#edges.append( (i,j) )

	# Print computation time
	time2 = time.time() - start_time
	exec_value = time2
	units = 'secs'
	if time2 > 60:
		time2 /= 60
		units = 'mins'
	if time2 > 60:
		time2 /= 60
		units = 'hours'

	time2 = round(time2,2)

	exec_value = "exec=" + str(time2) + " " + units

	print("--- " + str(time2) + " " + units + " ---")

	# Redraw points
	ax.clear()
	ax.set_xlim(0,1000)
	ax.set_ylim(0,1000)

	plot_data_lines(lines_x,lines_y)
	plot_data(ax, loc_x, loc_y, warehouses)

	ax.text(350, 1075, obj_value, family="serif", horizontalalignment='right', verticalalignment='top')
	ax.text(450, 1075, exec_value, family="serif", horizontalalignment='left', verticalalignment='top')
	fig.canvas.draw()