0

I am trying to solve a travelling salesman problem with Pyomo framework. However, I am stuck, as the solver is informing me that I have formulated it as infeasible.

    import numpy as np
    import pyomo.environ as pyo
    from pyomo.environ import *
    from pyomo.opt import SolverFactory

    journey_distances = np.array([[0, 28, 34, 45, 36],
                                  [28, 0, 45, 52, 64],
                                  [34, 45, 0, 11, 34],
                                  [45, 52, 11, 0, 34],
                                  [36, 64, 34, 34, 0]])


# create variables - integers

    num_locations = M.shape[0]
    model = pyo.ConcreteModel()
    model.journeys = pyo.Var(range(num_locations), range(num_locations), domain=pyo.Binary, bounds = (0,None))
    journeys = model.journeys
        

# add A to B constraints

    model.AtoB = pyo.ConstraintList()
    model.BtoA = pyo.ConstraintList()
    AtoB = model.AtoB
    BtoA = model.BtoA
    AtoB_sum = [sum([ journeys[i,j] for j in range(num_locations) if i!=j]) for i in range(num_locations)]
    BtoA_sum = [sum([ journeys[i,j] for i in range(num_locations) if j!=i]) for j in range(num_locations)]
    for journey_sum in range(num_locations):
        AtoB.add(AtoB_sum[journey_sum] == 1)
        if journey_sum <num_locations -1:
            BtoA.add(BtoA_sum[journey_sum] == 1)

# add auxilliary variables to ensure that each successive journey ends and starts on the same town. E.g. A to B, then B to C. 
# u_j - u_i >= -(n+1) + num_locations*journeys_{ij} for i,j = 1...n, i!=j 

    model.successive_aux = pyo.Var(range(0,num_locations), domain = pyo.Integers, bounds = (0,num_locations-1))
    model.successive_constr = pyo.ConstraintList()
    successive_aux = model.successive_aux
    successive_constr = model.successive_constr

    successive_constr.add(successive_aux[0] == 1)
    for i in range(num_locations):
        for j in range(num_locations):
            if i!=j:
                successive_constr.add(successive_aux[j] - successive_aux[i] >= -(num_locations - 1) + num_locations*journeys[i,j])

    obj_sum = sum([ sum([journey_distances [i,j]*journeys[i,j] for j in range(num_locations) if i!=j]) for i in range(num_locations)])
    model.obj = pyo.Objective(expr = obj_sum, sense = minimize)


    opt = SolverFactory('cplex')
    opt.solve(model)

    journey_res = np.array([model.journeys[journey].value for journey in journeys])
    print(journey_res)

# results output is:
print(results)

Problem: 
- Lower bound: -inf
  Upper bound: inf
  Number of objectives: 1
  Number of constraints: 31
  Number of variables: 26
  Number of nonzeros: 98
  Sense: unknown
Solver: 
- Status: ok
  User time: 0.02
  Termination condition: infeasible
  Termination message: MIP - Integer infeasible.
  Error rc: 0
  Time: 0.10198116302490234

# model.pprint() 

7 Set Declarations
    AtoB_index : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    5 : {1, 2, 3, 4, 5}
    BtoA_index : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    4 : {1, 2, 3, 4}
    journeys_index : Size=1, Index=None, Ordered=False
        Key  : Dimen : Domain                            : Size : Members
        None :     2 : journeys_index_0*journeys_index_1 :   25 : {(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)}
    journeys_index_0 : Size=1, Index=None, Ordered=False
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    5 : {0, 1, 2, 3, 4}
    journeys_index_1 : Size=1, Index=None, Ordered=False
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    5 : {0, 1, 2, 3, 4}
    successive_aux_index : Size=1, Index=None, Ordered=False
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    5 : {0, 1, 2, 3, 4}
    successive_constr_index : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :   21 : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}

2 Var Declarations
    journeys : Size=25, Index=journeys_index
        Key    : Lower : Value : Upper : Fixed : Stale : Domain
        (0, 0) :     0 :  None :     1 : False :  True : Binary
        (0, 1) :     0 :  None :     1 : False :  True : Binary
        (0, 2) :     0 :  None :     1 : False :  True : Binary
        (0, 3) :     0 :  None :     1 : False :  True : Binary
        (0, 4) :     0 :  None :     1 : False :  True : Binary
        (1, 0) :     0 :  None :     1 : False :  True : Binary
        (1, 1) :     0 :  None :     1 : False :  True : Binary
        (1, 2) :     0 :  None :     1 : False :  True : Binary
        (1, 3) :     0 :  None :     1 : False :  True : Binary
        (1, 4) :     0 :  None :     1 : False :  True : Binary
        (2, 0) :     0 :  None :     1 : False :  True : Binary
        (2, 1) :     0 :  None :     1 : False :  True : Binary
        (2, 2) :     0 :  None :     1 : False :  True : Binary
        (2, 3) :     0 :  None :     1 : False :  True : Binary
        (2, 4) :     0 :  None :     1 : False :  True : Binary
        (3, 0) :     0 :  None :     1 : False :  True : Binary
        (3, 1) :     0 :  None :     1 : False :  True : Binary
        (3, 2) :     0 :  None :     1 : False :  True : Binary
        (3, 3) :     0 :  None :     1 : False :  True : Binary
        (3, 4) :     0 :  None :     1 : False :  True : Binary
        (4, 0) :     0 :  None :     1 : False :  True : Binary
        (4, 1) :     0 :  None :     1 : False :  True : Binary
        (4, 2) :     0 :  None :     1 : False :  True : Binary
        (4, 3) :     0 :  None :     1 : False :  True : Binary
        (4, 4) :     0 :  None :     1 : False :  True : Binary
    successive_aux : Size=5, Index=successive_aux_index
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          0 :     0 :  None :     4 : False :  True : Integers
          1 :     0 :  None :     4 : False :  True : Integers
          2 :     0 :  None :     4 : False :  True : Integers
          3 :     0 :  None :     4 : False :  True : Integers
          4 :     0 :  None :     4 : False :  True : Integers

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : minimize : 28*journeys[0,1] + 34*journeys[0,2] + 45*journeys[0,3] + 36*journeys[0,4] + 28*journeys[1,0] + 45*journeys[1,2] + 52*journeys[1,3] + 64*journeys[1,4] + 34*journeys[2,0] + 45*journeys[2,1] + 11*journeys[2,3] + 34*journeys[2,4] + 45*journeys[3,0] + 52*journeys[3,1] + 11*journeys[3,2] + 34*journeys[3,4] + 36*journeys[4,0] + 64*journeys[4,1] + 34*journeys[4,2] + 34*journeys[4,3]

3 Constraint Declarations
    AtoB : Size=5, Index=AtoB_index, Active=True
        Key : Lower : Body                                                          : Upper : Active
          1 :   1.0 : journeys[0,1] + journeys[0,2] + journeys[0,3] + journeys[0,4] :   1.0 :   True
          2 :   1.0 : journeys[1,0] + journeys[1,2] + journeys[1,3] + journeys[1,4] :   1.0 :   True
          3 :   1.0 : journeys[2,0] + journeys[2,1] + journeys[2,3] + journeys[2,4] :   1.0 :   True
          4 :   1.0 : journeys[3,0] + journeys[3,1] + journeys[3,2] + journeys[3,4] :   1.0 :   True
          5 :   1.0 : journeys[4,0] + journeys[4,1] + journeys[4,2] + journeys[4,3] :   1.0 :   True
    BtoA : Size=4, Index=BtoA_index, Active=True
        Key : Lower : Body                                                          : Upper : Active
          1 :   1.0 : journeys[1,0] + journeys[2,0] + journeys[3,0] + journeys[4,0] :   1.0 :   True
          2 :   1.0 : journeys[0,1] + journeys[2,1] + journeys[3,1] + journeys[4,1] :   1.0 :   True
          3 :   1.0 : journeys[0,2] + journeys[1,2] + journeys[3,2] + journeys[4,2] :   1.0 :   True
          4 :   1.0 : journeys[0,3] + journeys[1,3] + journeys[2,3] + journeys[4,3] :   1.0 :   True
    successive_constr : Size=21, Index=successive_constr_index, Active=True
        Key : Lower : Body                                                           : Upper : Active
          1 :   1.0 :                                              successive_aux[0] :   1.0 :   True
          2 :  -Inf : -4 + 5*journeys[0,1] - (successive_aux[1] - successive_aux[0]) :   0.0 :   True
          3 :  -Inf : -4 + 5*journeys[0,2] - (successive_aux[2] - successive_aux[0]) :   0.0 :   True
          4 :  -Inf : -4 + 5*journeys[0,3] - (successive_aux[3] - successive_aux[0]) :   0.0 :   True
          5 :  -Inf : -4 + 5*journeys[0,4] - (successive_aux[4] - successive_aux[0]) :   0.0 :   True
          6 :  -Inf : -4 + 5*journeys[1,0] - (successive_aux[0] - successive_aux[1]) :   0.0 :   True
          7 :  -Inf : -4 + 5*journeys[1,2] - (successive_aux[2] - successive_aux[1]) :   0.0 :   True
          8 :  -Inf : -4 + 5*journeys[1,3] - (successive_aux[3] - successive_aux[1]) :   0.0 :   True
          9 :  -Inf : -4 + 5*journeys[1,4] - (successive_aux[4] - successive_aux[1]) :   0.0 :   True
         10 :  -Inf : -4 + 5*journeys[2,0] - (successive_aux[0] - successive_aux[2]) :   0.0 :   True
         11 :  -Inf : -4 + 5*journeys[2,1] - (successive_aux[1] - successive_aux[2]) :   0.0 :   True
         12 :  -Inf : -4 + 5*journeys[2,3] - (successive_aux[3] - successive_aux[2]) :   0.0 :   True
         13 :  -Inf : -4 + 5*journeys[2,4] - (successive_aux[4] - successive_aux[2]) :   0.0 :   True
         14 :  -Inf : -4 + 5*journeys[3,0] - (successive_aux[0] - successive_aux[3]) :   0.0 :   True
         15 :  -Inf : -4 + 5*journeys[3,1] - (successive_aux[1] - successive_aux[3]) :   0.0 :   True
         16 :  -Inf : -4 + 5*journeys[3,2] - (successive_aux[2] - successive_aux[3]) :   0.0 :   True
         17 :  -Inf : -4 + 5*journeys[3,4] - (successive_aux[4] - successive_aux[3]) :   0.0 :   True
         18 :  -Inf : -4 + 5*journeys[4,0] - (successive_aux[0] - successive_aux[4]) :   0.0 :   True
         19 :  -Inf : -4 + 5*journeys[4,1] - (successive_aux[1] - successive_aux[4]) :   0.0 :   True
         20 :  -Inf : -4 + 5*journeys[4,2] - (successive_aux[2] - successive_aux[4]) :   0.0 :   True
         21 :  -Inf : -4 + 5*journeys[4,3] - (successive_aux[3] - successive_aux[4]) :   0.0 :   True

13 Declarations: journeys_index_0 journeys_index_1 journeys_index journeys AtoB_index AtoB BtoA_index BtoA successive_aux_index successive_aux successive_constr_index successive_constr obj

If anyone can see what the problem is, and let me know, then that would be a great help.

AirSquid
  • 10,214
  • 2
  • 7
  • 31
  • after piecing this back together and executing it, the solver reports this is infeasible. Be sure to include & inspect this: `results = opt.solve(model) print(results)` Also, you are importing things twice, just do it once with the rename. And it is very confusing that you are renaming everything. Just leave the `model.` prefix, it will shorten your code a lot – AirSquid Jun 28 '21 at 22:41
  • Also, as you troubleshoot this, it would be a LOT easier if you choked it down to 3 points, printed your model with `model.pprint()` and manually checked your constraints, which are a bit tough to decipher. – AirSquid Jun 28 '21 at 22:49

2 Answers2

0

I'm not overly familiar w/ coding TSP problems, and I'm not sure of all the details in your code, but this (below) is a problem. It seems you are coding successive_aux (call it sa for short) as a sequencing of integers. In this snippet (I chopped down to 3 points), if you think about the legal route of 0-1-2-0, sa_1 > sa_0 and sa_2 > sa_1, then it is infeasible to require sa_0 > sa_2. Also, your bounds on sa appear infeasible as well. In this example, sa_0 is 1, and the upper bound on sa is 2. Those are 2 "infeasibilities" in your formulation.

  Key : Lower : Body                                                           : Upper : Active
          1 :   1.0 :                                              successive_aux[0] :   1.0 :   True
          2 :  -Inf : -2 + 3*journeys[0,1] - (successive_aux[1] - successive_aux[0]) :   0.0 :   True
          3 :  -Inf : -2 + 3*journeys[0,2] - (successive_aux[2] - successive_aux[0]) :   0.0 :   True
          4 :  -Inf : -2 + 3*journeys[1,0] - (successive_aux[0] - successive_aux[1]) :   0.0 :   True
          5 :  -Inf : -2 + 3*journeys[1,2] - (successive_aux[2] - successive_aux[1]) :   0.0 :   True
          6 :  -Inf : -2 + 3*journeys[2,0] - (successive_aux[0] - successive_aux[2]) :   0.0 :   True
          7 :  -Inf : -2 + 3*journeys[2,1] - (successive_aux[1] - successive_aux[2]) :   0.0 :   True
AirSquid
  • 10,214
  • 2
  • 7
  • 31
0

I'm not an optimization expert but it looks like you need to change the distances between the cities since you're basically saying that the distance from city1 to city1 = 0, city2 to city2 = 0 etc. If you change these distances to a very large number (say 1000000) the optimizer will never pick to go from city1 back to city1. Hope this helps.