Esempio n. 1
0
class CompactFormulation:
    """
    This class implements the proposed formulation using Gurobi.
    """

    def __init__(self, inst, big_m=None, murray_rules=False):
        if not big_m:
            big_m = inst.big_m if inst.big_m else 1000

        model = Model()
        L = Ell(inst)

        x = {(i, j, ell): model.add_var(obj=0, var_type=BINARY,
                                        name="x({i},{j}__{ell})".format(**locals()))
             for (i, j) in inst.A
             for ell in L.list}

        y = {(i, j, k, ell, ell_): model.add_var(obj=0,
                                                 var_type=BINARY,
                                                 name="y({i},{j},{k}__{ell},{ell_})".format(**locals()))
             for (i, j, k) in inst.D
             for ell in L.list
             for ell_ in L.list_ell_prime(ell)}

        t = [model.add_var(obj=0,
                           name="t({ell})".format(**locals()))
             for ell in L.list]
        t[0].ub = 0
        t.append(model.add_var(obj=1, name="t_total".format(**locals())))

        # depot constraints
        model.add_constr(xsum(x[0, j, 0]
                              for j in inst.V if (0, j) in inst.A)
                         == xsum(x[j, 0, ell]
                                 for j in inst.V if (j, 0) in inst.A
                                 for ell in L.list[1:]),
                         "depot")
        model.add_constr(xsum(x[0, j, 0]
                              for j in inst.V if (0, j) in inst.A) == 1,
                         "depot_eq_1")

        # single customer visit
        for i in inst.V:
            model.add_constr(xsum(x[i, j, ell]
                                  for j in inst.V if (i, j) in inst.A
                                  for ell in L.list) <= 1,
                             "single_position_in({i})".format(**locals()))
            model.add_constr(xsum(x[j, i, ell]
                                  for j in inst.V if (j, i) in inst.A
                                  for ell in L.list) <= 1,
                             "single_position_out({i})".format(**locals()))

        # flow preservation constraints
        for k in inst.V_:
            for ell in L.list[1:]:
                model.add_constr(xsum(x[j, k, ell - 1]
                                      for j in inst.V if (j, k) in inst.A)
                                 == xsum(x[k, j, ell]
                                         for j in inst.V if (k, j) in inst.A),
                                 "flow({k},{ell})".format(**locals()))

        # one arc per position constraints
        for ell in L.list:
            model.add_constr(xsum(x[i, j, ell]
                                  for (i, j) in inst.A)
                             <= 1, "one_arc({ell})".format(**locals()))

        # one drone per position constraints
        for ell in L.list:
            model.add_constr(xsum(y[i, k, j, l, l_]
                                  for (i, k, j) in inst.D
                                  for l in L.list[:ell + 1]
                                  for l_ in L.list[ell + 1:l + 1 + L.max_l])
                             <= 1, "one_drone({ell})".format(**locals()))

        # all customers must be visited constraints
        for k in inst.V_:
            model.add_constr(xsum(x[k, j, l]
                                  for j in inst.V if (k, j) in inst.A
                                  for l in L.list)
                             + xsum(y[i, k, j, l, l_]
                                    for i in inst.V
                                    for j in inst.V if (i, k, j) in inst.D
                                    for l in L.list
                                    for l_ in L.list_ell_prime(l))
                             == 1, "all_customers({k})".format(**locals()))

        # drone launch constraints
        for i in inst.V:
            for ell in L.list:
                model.add_constr(xsum(y[i, k, j, ell, l_]
                                      for k in inst.V_
                                      for j in inst.V if (i, k, j) in inst.D
                                      for l_ in L.list_ell_prime(ell))
                                 <= xsum(x[i, j, ell]
                                         for j in inst.V if (i, j) in inst.A),
                                 "drone_launch({i},{ell})".format(**locals()))

        # drone return constraints
        for j in inst.V:
            for ell_ in L.list[1:]:
                model.add_constr(xsum(y[i, k, j, l, ell_]
                                      for i in inst.V
                                      for k in inst.V_ if (i, k, j) in inst.D
                                      for l in L.list_ell(ell_))
                                 <= xsum(x[i, j, ell_ - 1]
                                         for i in inst.V if (i, j) in inst.A),
                                 "drone_return({j},{ell_})".format(**locals()))

        # maximum drone endurance constraints
        for idx, ell in enumerate(L.list[1:]):
            if murray_rules:  # and idx == 0:
                continue
            for ell_ in L.list_ell_prime(ell):
                model.add_constr(t[ell_] - t[ell] <= inst.E
                                 + big_m * (1 - xsum(y[i, k, j, ell, ell_]
                                                     for (i, k, j) in inst.D)),
                                 "endurance({ell},{ell_})".format(**locals()))

        # time constraints considering only the truck and drone setup time
        for ell in L.list[1:] + [len(L.list)]:
            model.add_constr(t[ell] >= t[ell - 1]
                             + xsum(inst.tau_truck[i][j] * x[i, j, ell - 1]
                                    for (i, j) in inst.A)
                             + inst.sl * (xsum(y[i, k, j, ell - 1, ell_]
                                               for (i, k, j) in inst.D for ell_ in L.list_ell_prime(ell - 1) if i != 0))
                             + inst.sr * (xsum(y[i, k, j, ell_, ell]
                                               for (i, k, j) in inst.D for ell_ in L.list_ell(ell))),
                             "time_truck_only({ell})".format(**locals()))

        # time constraints accounting drone's time
        for ell_ in L.list[1:]:
            for ell in L.list_ell(ell_):
                model.add_constr(t[ell_] >= t[ell]
                                 + xsum((inst.tau_drone[i][k] + inst.tau_drone[k][j] + (0 if murray_rules else inst.sl) + inst.sr)
                                        * y[i, k, j, ell, ell_]
                                        for (i, k, j) in inst.D),
                                 "time_drones({ell},{ell_})".format(**locals()))

        # creating class variables
        self.inst = inst
        self.big_m = big_m
        self.murray_rules = murray_rules
        self.model = model
        self.L = L
        self.x = x
        self.y = y
        self.t = t
        self.solution = None

    def optimize(self, timelimit, sol=None):
        """
        Solves the compact formulation considering the time limit and the initial solution
        passed as arguments
        """
        if sol:
            mip_start = []
            # reading initial solution file
            initial_solution = Solution(self.inst, self.murray_rules)
            initial_solution.read(sol)

            # setting initial truck path
            i = initial_solution.truck_path[0]
            for ell, j in enumerate(initial_solution.truck_path[1:]):
                mip_start.append((self.x[i, j, ell], 1.0))
                i = j

            # setting initial drone paths
            for (i, j, k) in initial_solution.drone_paths:
                ell = initial_solution.truck_path.index(i)
                ell_ = initial_solution.truck_path.index(k) if k != 0 else len(initial_solution.truck_path) - 1
                mip_start.append((self.y[i, j, k, ell, ell_], 1.0))

            self.model.start = mip_start

        self.model.write("logs/fstsp.lp")
        self.model.optimize(max_seconds=timelimit)

        # creating final solution
        self.solution = Solution(self.inst, cost=self.model.objective_value, murray_rules=self.murray_rules)
        for key in [key for key in self.x.keys() if self.x[key].x > EPS]:
            self.solution.add_truck_arc((key[0], key[1]))
        for key in [key for key in self.y.keys() if self.y[key].x > EPS]:
            self.solution.add_drone_visit((key[0], key[1], key[2]))
Esempio n. 2
0
class MurrayFormulation:
    def __init__(self, inst, big_m=5000):
        model = Model()

        x = {(i, j): model.add_var(obj=0, var_type=BINARY,
                                   name='x({i},{j})'.format(**locals()))
             for (i, j) in inst.A}

        y = {(i, k, j): model.add_var(obj=0, var_type=BINARY,
                                      name='y({i},{k},{j})'.format(**locals()))
             for (i, k, j) in inst.D}

        u = {i: model.add_var(obj=0, var_type=INTEGER, lb=1, ub=len(inst.V_) + 2,
                              name="u({i})".format(**locals()))
             for i in inst.V}

        p = {(i, j): model.add_var(obj=0, var_type=BINARY, lb=1 if i == 0 else 0,
                                   name="p({i},{j})".format(**locals()))
             for (i, j) in inst.A}

        t = {i: model.add_var(obj=0 if i != inst.V[-1] else 1, lb=0,
                              name='t({i})'.format(**locals()))
             for i in inst.V}
        t[0].ub = 0

        t_ = {i: model.add_var(obj=0, lb=0,
                               name='t_prime({i})'.format(**locals()))
              for i in inst.V}
        t_[0].ub = 0

        model.add_constrs((quicksum(x[i, j]
                                    for i in inst.V[:-1] if (i, j) in inst.A) +
                           quicksum(y[i, j, k]
                                    for i in inst.V[:-1]
                                    for k in inst.V[1:] if (i, j, k) in inst.D)
                           == 1 for j in inst.V_), "c2")

        model.add_constr(quicksum(x[0, j]
                                  for j in inst.V[1:] if (0, j) in inst.A)
                         == 1, "c3")

        model.add_constr(quicksum(x[i, inst.V[-1]]
                                  for i in inst.V[:-1] if (i, inst.V[-1]) in inst.A)
                         == 1, "c4")

        model.add_constrs((u[i] - u[j] + 1 <= (len(inst.V_) + 2) * (1 - x[i, j])
                           for (i, j) in inst.A if i != 0), "c5")

        model.add_constrs((quicksum(x[i, j]
                                    for i in inst.V[:-1] if (i, j) in inst.A)
                           == quicksum(x[j, k]
                                       for k in inst.V[1:] if (j, k) in inst.A)
                           for j in inst.V_), "c6")

        model.add_constrs((quicksum(y[i, j, k]
                                    for j in inst.V_
                                    for k in inst.V[1:]
                                    if i != j != k and (i, j, k) in inst.D) <= 1
                           for i in inst.V[:-1]), "c7")

        model.add_constrs((quicksum(y[i, j, k]
                                    for i in inst.V[:-1]
                                    for j in inst.V_
                                    if i != j != k and (i, j, k) in inst.D) <= 1
                           for k in inst.V[1:]), "c8")

        model.add_constrs((2 * y[i, j, k]
                           <= quicksum(x[h, i]
                                       for h in inst.V[:-1] if (h, i) in inst.A)
                           + quicksum(x[l, k]
                                      for l in inst.V_ if (l, k) in inst.A)
                           for i in inst.V_
                           for j in inst.V_
                           for k in inst.V[1:] if (i, j, k) in inst.D), "c9")

        model.add_constrs((y[0, j, k] <= quicksum(x[h, k]
                                                  for h in inst.V[:-1] if (h, k) in inst.A)
                           for j in inst.V_
                           for k in inst.V[1:] if (0, j, k) in inst.D), "c10")

        model.add_constrs((u[k] - u[i]
                           >= 1 - (len(inst.V_) + 2) * (1 - quicksum(y[i, j, k]
                                                                     for j in inst.V_ if (i, j, k) in inst.D))
                           for i in inst.V_
                           for k in inst.V[1:] if (i, k) in inst.A), "c11")

        model.add_constrs((t_[i]
                           >= t[i] - big_m * (1 - quicksum(y[i, j, k]
                                                           for j in inst.V_
                                                           for k in inst.V[1:]
                                                           if i != j != k and (i, j, k) in inst.D))
                           for i in inst.V_), "c12")

        model.add_constrs((t_[i]
                           <= t[i] + big_m * (1 - quicksum(y[i, j, k]
                                                           for j in inst.V_
                                                           for k in inst.V[1:]
                                                           if i != j != k and (i, j, k) in inst.D))
                           for i in inst.V_), "c13")

        model.add_constrs((t_[k]
                           >= t[k] - big_m * (1 - quicksum(y[i, j, k]
                                                           for i in inst.V[:-1]
                                                           for j in inst.V_
                                                           if i != j != k and (i, j, k) in inst.D))
                           for k in inst.V[1:]), "c14")

        model.add_constrs((t_[k]
                           <= t[k] + big_m * (1 - quicksum(y[i, j, k]
                                                           for i in inst.V[:-1]
                                                           for j in inst.V_
                                                           if i != j != k and (i, j, k) in inst.D))
                           for k in inst.V[1:]),
                          "c15")

        model.add_constrs((t[k] >= t[h] + inst.tau_truck[h][k]
                           + inst.sl * (quicksum(y[k, l, m]
                                                 for l in inst.V_
                                                 for m in inst.V[1:]
                                                 if k != l != m and (k, l, m) in inst.D))
                           + inst.sr * (quicksum(y[i, j, k]
                                                 for i in inst.V[:-1]
                                                 for j in inst.V_
                                                 if i != j != k and (i, j, k) in inst.D))
                           - (big_m * (1 - x[h, k]))
                           for h in inst.V[:-1]
                           for k in inst.V[1:] if k != h), "c16")

        model.add_constrs((t_[j] >= t_[i] + inst.tau_drone[i][j]
                           - big_m * (1 - quicksum(y[i, j, k]
                                                   for k in inst.V[1:] if (i, j, k) in inst.D))
                           for j in inst.V_drone
                           for i in inst.V[:-1] if i != j), "c17")

        model.add_constrs((t_[k] >= t_[j] + inst.tau_drone[j][k] + inst.sr
                           - big_m * (1 - quicksum(y[i, j, k]
                                                   for i in inst.V[:-1] if (i, j, k) in inst.D))
                           for j in inst.V_drone
                           for k in inst.V[1:] if j != k), "c18")

        model.add_constrs((t_[k] - (t_[j] - inst.tau_drone[i][j])
                           <= inst.E + big_m * (1 - y[i, j, k])
                           for (i, j, k) in inst.D),
                          "c19")

        model.add_constrs((u[i] - u[j] >= 1 - (len(inst.V_) + 2) * p[i, j]
                           for i in inst.V_
                           for j in inst.V_ if j != i), "c20")

        model.add_constrs((u[i] - u[j] <= -1 + (len(inst.V_) + 2) * (1 - p[i, j])
                           for i in inst.V_
                           for j in inst.V_ if j != i), "c21")

        model.add_constrs((p[i, j] + p[j, i] == 1
                           for i in inst.V_
                           for j in inst.V_ if j != i), "c22")

        model.add_constrs((t_[l] >= t_[k]
                           - big_m * (3 - quicksum(y[i, j, k]
                                                   for j in inst.V_ if j != l and (i, j, k) in inst.D)
                                      - quicksum(y[l, m, n]
                                                 for m in inst.V_
                                                 for n in inst.V[1:]
                                                 if m != i and m != k and m != l
                                                 and n != i and n != k and (l, m, n) in inst.D)
                                      - p[i, l])
                           for i in inst.V[:-1]
                           for k in inst.V[1:]
                           for l in inst.V_
                           if k != i and l != i and l != k),
                          "c23")

        # creating class variables
        self.inst = inst
        self.big_m = big_m
        self.murray_rules = True
        self.model = model
        self.x = x
        self.y = y
        self.p = p
        self.u = u
        self.t = t
        self.t_ = t_
        self.solution = None

    def optimize(self, timelimit, sol=None):
        """
        Solves the compact formulation considering the time limit given as argument.
        """
        if sol:
            mip_start = []

            # reading initial solution file
            initial_solution = Solution(self.inst, murray_rules=self.murray_rules)
            initial_solution.read(sol)

            # setting initial truck path
            i = initial_solution.truck_path[0]
            for ell, j in enumerate(initial_solution.truck_path[1:]):
                j = j if j > 0 else self.inst.V[-1]
                mip_start.append((self.x[i, j], 1.0))
                i = j

            # setting initial drone paths
            for (i, j, k) in initial_solution.drone_paths:
                k = k if k > 0 else self.inst.V[-1]
                mip_start.append((self.y[i, j, k], 1.0))

            self.model.start = mip_start

        self.model.write('logs/murray_fstsp.lp')
        self.model.optimize(max_seconds=timelimit)

        # creating final solution
        self.solution = Solution(self.inst, cost=self.model.objective_value, murray_rules=self.murray_rules)
        for key in [key for key in self.x.keys() if self.x[key].x > EPS]:
            self.solution.add_truck_arc((key[0], key[1] if key[1] != self.inst.V[-1] else 0))
        for key in [key for key in self.y.keys() if self.y[key].x > EPS]:
            self.solution.add_drone_visit((key[0], key[1], key[2] if key[2] != self.inst.V[-1] else 0))