•  Gurobi Staff

Do I understand correctly that both logs should belong to the same model? This is not the case. The second log shows a different objective

Best objective 8.269768433600e+07, best bound 8.268332877545e+07, gap 0.0174%

vs

Best objective 8.333525193000e+07, best bound 8.333524871943e+07, gap 0.0000%

Also, while the statistics for the presolved models look equivalent, the initial model in the second log contains more rows, columns, and variables. If I understand correctly the models should only differ in some parameter but not in the initial size.

Do you know the Multiple Scenarios feature of Gurobi that is designed for doing some sensitive analysis? The advantage of this feature is that all variations of one model are solved in one run. See also the Gurobi Python example multiscenario.py

•   The second model is built based on the first model, with the only change being adding the percentage_variations and the for-loop. The input parameters are the same. I thought for the iteration where percentage_variations is 0, there objective would be the same, since there is no change in input parameter values and constraints etc.

Regarding multiscenario.py, if I have the following parameters and constraints:

a = [0.36, 0.27, 0.44, 0.46, 0.42]b = [0.19, 0.28, 0.23, 0.27, 0.2]c = [0.23, 0.18, 0.29, 0.14, 0.27]x = model.addVar(vtype=gp.GRB.CONTINUOUS, name="x") y = model.addVar(vtype=gp.GRB.CONTINUOUS, name="y") z = model.addVar(vtype=gp.GRB.CONTINUOUS, name="z")for t in len(a):    model.addConstr(x + y <= 5 * a[t] name="constraint1")     model.addConstr(2 * x - y <= 8 * b[t], name="constraint2")    model.addConstr(3 * x + 2 * y >= 10 * c[t], name="constraint2")...

In one scenario, the range of values of a should be changed to random.uniform(0.5,0.8), for b it should be random.uniform(0.2,0.4) and c random.uniform(0.1,0.2). Could this also be implemented using multiscenario?

•  Gurobi Staff

As I understand it you want to change the RHS of constraints1 in a first scenario, the RHS of constraints2 in a second scenario, and the RHS of constraints3 in a third scenario.
This could be done as follows

constr1 = model.addConstrs((x + y <= 5 * a[t] for t in range(len(a))), name="constraint1")constr2 = model.addConstrs((2 * x - y <= 8 * b[t] for t in range(len(b))), name="constraint2")constr3 = model.addConstrs((3 * x + 2 * y >= 10 * c[t] for t in range(len(c))), name="constraint3")model.NumScenarios = 4# Scenario 0: Base model, hence, nothing to do except giving the scenario a namemodel.Params.ScenarioNumber = 0model.ScenNName = 'Base model'# Scenario 1: change RHS of constr1 to be random numbers between 0.5 and 0.8model.Params.ScenarioNumber = 1model.ScenNName = 'change constr1'for t in range(len(a)):    constr1[t].ScenNRhs = np.random.uniform(0.5,0.8)# Scenario 2: change RHS of constr2model.Params.ScenarioNumber = 2model.ScenNName = 'change constr2'for t in range(len(b)):    constr2[t].ScenNRhs = np.random.uniform(0.2,0.4)# Scenario 3: change RHS of constr3model.Params.ScenarioNumber = 3model.ScenNName = 'change constr3'for t in range(len(c)):    constr3[t].ScenNRhs = np.random.uniform(0.1,0.2)

For general debugging, I would recommend writing your model into an LP-file using Model.write() before optimizing to check if this is indeed the model you intend to solve.
•   Thank you so much for your help.

Can you explain briefly what ScenNRhs does? I am not certain how the ScenNRhs behaves. The documentation says it changes the RHS of the linear constraint. But if that is the case, wouldn't constr1[t].ScenNRhs = np.random.uniform(0.5,0.8) make it so that for scenario 1, constraint 1 becomes constr1 = model.addConstrs((x + y <= np.random.uniform(0.5,0.8)) ?
If that is the case, how should a constraint with multiple variables on both sides be modified for different scenarios? For example

constr1 = model.addConstrs((x + y == z * a[t] for t in range(len(a))), name="constraint_1")
•  Gurobi Staff

To better understand how the attribute ScenNRHS can be used, you need to understand what the right-hand side (RHS) of a constraint is. Consider the constraint $$x + y - 5z = 2$$. It can equivalently be written e.g. as $$x + y - 2 = 5z$$. But both represent the same constraint and the RHS is 2. To identify the RHS, you need to transform the constraint s.t. all variables are on the left and all constants are on the right (which is the case for the first expression).
(You could also check the attribute RHS.)
If you have constraint $$x + y - 5z = 2$$ and want to consider the alternative constraint  $$x + y - 5z = 3$$  in a multiple-scenario setting, you can do this by changing the attribute ScenNRHS from 2 to 3 of this constraint.

Let's consider your type of example: Constraint $$x + y = 2z$$ can be written as $$x + y - 2z = 0$$. The RHS is 0. Now you do not want to change the RHS but the coefficient of variable $$z$$. This is not as easy as changing the RHS and needs a trick. Assume in a second scenario you want to consider the constraint  $$x + y = 3z$$. This can be done as follows:

# reformulate == constraint as two <= and >= and constraintsconstrBaseLT = model.addConstr(x+y-2*z <= 0)constrBaseGT = model.addConstr(x+y-2*z >= 0)# add the constraints for the different scenario from the beginning, but de-activate them# de-activate here means that the constraints are always satisfiedconstrScenLT = model.addConstr(x+y-3*z <= gp.GRB.INFINITY)constrScenGT = model.addConstr(x+y-3*z >= -gp.GRB.INFINITY)model.NumScenarios = 2# Scenario 0: Base model, hence, nothing to do except giving the scenario a namemodel.Params.ScenarioNumber = 0model.ScenNName = 'Base model'# Scenario 1: activate the alternative constraints and de-activate the original onesconstrBaseLT.ScenNRHS = gp.GRB.INFINITYconstrBaseGT.ScenNRHS = -gp.GRB.INFINITYconstrScenLT.ScenNRHS = 0constrScenGT.ScenNRHS = 0

Please also have a look at Tips and Tricks for multiple scenarios.