Python: use results of a solution

imagen

I’m using amplpy. The picture above is the results of variable X of a model1 solved.

How can I set in model2 this restriction:
x[1,1,1]=0 ?

I’m trying to use the result of model1 as restriction of a model2.

Thanks

1 Like

Hi @juan.carlos,

You can fix that specific variable with:

x = ampl.get_variable("x")
x[1, 1, 1].fix(0)

However, if you want to fix all indices with ampl.get_variable("x").fix() after setting the variable values:

x = ampl.get_variable("x")
x.set_values(...)
x.fix()

I have a problem. I solve the first model, fix the variable results, but when I want to solve model2, it’s says that the restrictions are already used. Model2 variables, paramaters and restrictions have the same name as model1, the difference is that in model1 I solve round1 of a tournamente and in model 2, I solve round2.

Is there a parameter I can define model2 ? If I put “reset;” then values I fixed after solving model1 are lost.

Hi @juan.carlos,

One way to do that would be to use multiple problem definitions in the same AMPL session. However, it may be cleaner to do that using two AMPL objects as follows:

model1 = AMPL()
model1.eval(r"""
set I := 1..10;
var x{I} integer >= 0;
param v{i in I} := 1 + Irand224() mod 200;
param w{i in I} := 1 + Irand224() mod 200;
maximize profit: sum{i in I} v[i] * x[i];
s.t. capacity: sum{i in I} w[i] * x[i] <= 1000;
""")
model1.solve(solver="highs", highs_options="outlev=1")
model1.display("x")

model2 = AMPL()
model2.eval(r"""
set I := 1..10;
param a{I};
""")
model2.param["a"] = prob1.var["x"].get_values() # load the values from variable x in model1 into param a of model2
model2.display("a")

You can see that in model1, I solve the values for a variable x[1…40,1…10,1…4]. Then in order to solve model2, I must insert the values of variable x[1…40,1…10,1…4, 1] as restrictions of the model. X is binary.

The models are really similar, the big difference is that in model1, the x variable has 3 indices while in model2 the x variable has 4 indices. I was solving model1 and typing the results as restrictions in model2, but it too time consuming. I want to automatize this.

For example, imagine that in model1 the solution is that x[4,1,4]=1. Then in model2, I must insert a new restriction:
s.t x[4,1,4,1]=1

Attached both models and data for them.
data.dat (560 Bytes)
ronda1.mod (5.1 KB)
ronda2.mod (7.6 KB)

You can do that by retrieving the values from the first solution, convert it to a pandas dataframe and add another index with value 1 for all entries.

from amplpy import AMPL

model1 = AMPL()
model1.read("ronda1.mod")
model1.read_data("data.dat")
model1.option["gurobi_options"] = "outlev=1 timelim=2"
model1.solve(solver="gurobi")

x_values = model1.var["x"].to_pandas()
x_values.reset_index(inplace=True) # reset the index so that indices become regular columns
x_values["index3"] = 1 # add new column with value 1 for every entry
x_values.set_index(["index0", "index1", "index2", "index3"], inplace=True) # set indices again

model2 = AMPL()
model2.read("ronda2.mod")
model2.read_data("data.dat")
model2.var["x"] = x_values
model2.eval(r"fix {a in A, d in D, t in T} x[a, d, t, 1];") # fix x[a, d, t, 1]
model2.option["gurobi_options"] = "outlev=1 timelim=2"
model2.solve(solver="gurobi")

Since the models are not that different, merging them to be able to have just one model could simplify this.

1 Like

It worked ! Thanks :grinning:

I want to extend it to a solution pool for model 1, as not every solution of model1 is a factible solution for model2. The solution pool is just for model1. I was thinking something like this. Repeat the process for everysolution in the pool:

model1 = AMPL()
model1.read(“ronda1.mod”)
model1.read_data(“data.dat”)
model1.option[“cplex_options”] = “poolstub=obj poolcapacity=5 poolreplace=1 timelimit=60”
model1.solve(solver=“cplex”)
model1.display(‘x’)

for n {in 1…obj.npool} {

x_values = model1.var[“x”].to_pandas()
x_values.reset_index(inplace=True) # reset the index so that indices become regular columns
x_values[“index3”] = 1 # add new column with value 1 for every intry
x_values.set_index([“index0”, “index1”, “index2”, “index3”], inplace=True) # set indices again

model2 = AMPL()
model2.read(“ronda2.mod”)
model2.read_data(“data.dat”)
model2.var[“x”] = x_values
model2.eval(r"fix {a in A, d in D, t in T} x[a, d, t, 1];") # fix x[a, d, t, 1];
model2.eval(“display x.status;”)
model2.option[“cplex_options”] = “outlev=1 timelim=60”
model2.solve(solver=“cplex”)

model2.eval(r" x >(“obj” & n & “.out”) ;") #save solution in a .out file
}

I think that by only including “n” in this statement is sufficient:

x_values = model1.var[“x”].to_pandas()

You can iterate through the solutions and load each of them with:

for i in range(1, model1.get_value("obj.npool") + 1):
    model1.eval(f"solution obj{i}.sol;")

The entire code would be the following:

from amplpy import AMPL

# Load first model
model1 = AMPL()
model1.read("ronda1.mod")
model1.read_data("data.dat")
model1.option["cplex_options"] = (
    "mipdisplay=1 poolstub=obj poolcapacity=5 poolreplace=1 timelimit=10"
)
model1.solve(solver="cplex")

# Load second model
model2 = AMPL()
model2.read("ronda2.mod")
model2.read_data("data.dat")
model2.eval(r"fix {a in A, d in D, t in T} x[a, d, t, 1];")

for i in range(1, model1.get_value("obj.npool") + 1):
    model1.eval(f"solution obj{i}.sol;")
    x_values = model1.var["x"].to_pandas()  # export values for x in solution number i
    x_values.reset_index(inplace=True)
    x_values["index3"] = 1
    x_values.set_index(["index0", "index1", "index2", "index3"], inplace=True)

    model2.var["x"] = x_values  # set values for x using solution number i
    model2.option["cplex_options"] = "mipdisplay=1 timelimit=10"
    model2.solve(solver="cplex")

How do I print the solution in a .out file ? Is the following ok ?

from amplpy import AMPL

Load first model

model1 = AMPL()
model1.read(“ronda1.mod”)
model1.read_data(“data.dat”)
model1.option[“cplex_options”] = (“mipdisplay=1 poolstub=obj poolcapacity=5 poolreplace=1 timelimit=10”)
model1.solve(solver=“cplex”)

Load second model

model2 = AMPL()
model2.read(“ronda2.mod”)
model2.read_data(“data.dat”)
model2.eval(r"fix {a in A, d in D, t in T} x[a, d, t, 1];")

for i in range(1, model1.get_value(“obj.npool”) + 1):
model1.eval(f"solution obj{i}.sol;")
x_values = model1.var[“x”].to_pandas() # export values for x in solution number i
x_values.reset_index(inplace=True)
x_values[“index3”] = 1
x_values.set_index([“index0”, “index1”, “index2”, “index3”], inplace=True)
model2.var[“x”] = x_values # set values for x using solution number i
model2.option[“cplex_options”] = “mipdisplay=1 timelimit=10”
model2.solve(solver=“cplex”)
model2.eval(r"“” display x>“x{i}.out”;“”")

You need to use a f-string as follows:

model2.eval(f"""display x>"x{i}.out";""")

Thanks ! It worked.

Is it possible to print in that .out file the relative gap of the solution?

I think that the field is “relmipgap”.

You need to add return_mipgap=1 to cplex_options and then you can write it with model2.eval(f"""display obj.relmipgap>"x{i}.out";""").

...
for i in range(1, model1.get_value("obj.npool") + 1):
    model1.eval(f"solution obj{i}.sol;")
    x_values = model1.var["x"].to_pandas()
    x_values.reset_index(inplace=True)
    x_values["index3"] = 1
    x_values.set_index(["index0", "index1", "index2", "index3"], inplace=True)

    model2.var["x"] = x_values
    model2.option["cplex_options"] = "mipdisplay=1 timelimit=10 return_mipgap=1"  # set return_mipgap=1
    model2.solve(solver="cplex", verbose=True)

    model2.eval(f"""display x>"x{i}.out";""")
    model2.eval(f"""display obj.relmipgap>"x{i}.out";""")  # write obj.relmipgap

Is it possible to do this ?

Since when relmigap=infinity, it means that there’s no factible solution. I don’t want to print files for these solutions.

You can use model2.get_value("obj.relmipgap") to access the value of obj.relmipgap from Python:

if model2.get_value("obj.relmipgap") < 1:
    model2.eval(f"""display x>"x{i}.out";""")
    model2.eval(f"""display obj.relmipgap>"x{i}.out";""")

How is it possible to save the log (nohup) of this part in a file ?

Thanks Filipe

Hi @juan.carlos,

You can do that by creating an OutputHandler that collects all output as follows:

from amplpy import AMPL, OutputHandler


class CollectOutput(OutputHandler):
    def __init__(self):
        self.buffer = ""

    def output(self, kind, msg):
        self.buffer += msg


output_handler = CollectOutput(). # instantiate the output handler

model1 = AMPL()
ampl.set_output_handler(output_handler)  # set the output handler
model1.read("ronda1.mod")
model1.read_data("data.dat")
model1.option[“cplex_options”] = ("timing=1 lpdisplay=2 mipdisplay=2 poolstub=obj poolcapacity=5 poolreplace=1 timelimit=10")
model1.solve(solver="cplex")

# save the output in output.txt
open("output.txt", "w").write(output_handler.buffer)