Computing the gradient of an objective function evaluated at the optimum in a dynamic optimization problem, pyomo - pyomo

I am computing the solution to a dynamic non-linear optimization problem, that I set up usign the pyomo library. I use a ConcreteModel, with an objective function and several constraints, all time-indexed.
My objective function takes the form of a ScalarObjective (I am solving a dynamic general equilibrium problem in which I seek to maximize total welfare). I would like to compute the gradient of the objective, evaluated at the optimum, with respect to one of the model's variables at a given period t. My problem is a discrete-time problem.
I have tried many different options, asking AI chatbots for help (both You Chat and ChatGPT), but every solution I'm given is incorrect -- on this topic the AI chatbots seem to know very little.
I feel that some method in the library pyomo.dae could be of help, but I haven't found a solution yet. Could anyone help me, please?

You can do this using Pyomo's differentiate function. Here is a toy example:
import pyomo.environ as pyo
from pyomo.core.expr.calculus.derivatives import differentiate
m = pyo.ConcreteModel()
m.x = pyo.Var()
m.con = pyo.Constraint(expr=m.x<=10)
m.obj = pyo.Objective(expr=m.x**2)
pyo.SolverFactory('ipopt').solve(m)
print(pyo.value(m.x))
# -1.2528349584581178e-10
# Evaluate the derivative at current value of m.x
ddx = differentiate(m.obj, wrt=m.x)
print(ddx)
# -2.5056699169162357e-10
# Return derivative expression
ddx2 = differentiate(m.obj, wrt=m.x, mode='sympy')
print(ddx2)
# 2.0*x
You can read more about this function here: https://github.com/Pyomo/pyomo/blob/main/pyomo/core/expr/calculus/derivatives.py#L31

Related

How to know if the optimization problem is infeasible or not? Pyomo Warning: Problem may be infeasible

Pyomo can find a solution, but it gives this warning:
WARNING: Loading a SolverResults object with a warning status into
model=(SecondCD);
message from solver=Ipopt 3.11.1\x3a Converged to a locally infeasible point. Problem may be infeasible.
How do I know if the problem is infeasible or not?
this pyomo model optimizes a farm's decision of inputs allocation.
model.Crops = Set() # set Crops := cereal rapes maize ;
model.Inputs = Set() # set Inputs := land labor capital fertilizer;
model.b = Param(model.Inputs) # Parameters in CD production function
model.x = Var(model.Crops, model.Inputs, initialize = 100, within=NonNegativeReals)
def production_function(model, i):
return prod(model.x[i,j]**model.b[j] for j in model.Inputs)
model.Q = Expression(model.Crops, rule=production_function)
...
instance = model.create_instance(data="SecondCD.dat")
opt = SolverFactory("ipopt")
opt.options["tol"] = 1E-64
results = opt.solve(instance, tee=True) # solves and updates instance
instance.display()
if I set b >=1, (e.g.: param b := land 1 labor 1 capital 1 fertilizer 1),
pyomo can find optimal solution;
but if i set b < 1, (e.g.: param b := land 0.1 labor 0.1 capital 0.1 fertilizer 0.1), and set opt.options["tol"] = 1E-64, pyomo can find a solution, but gives that warning.
I expect an optimal solution, but the actual result gives the warning mentioned above.
The message you get (message from solver=Ipopt 3.11.1\x3a Converged to a locally infeasible point. Problem may be infeasible.) doesn't mean that the problem is necessarilly infeasible. A non-linear solver will typically give you a local optimum, and the path to get to the solution is a very important part of finding a "better" local optimum. When you tried with another point, you found a feasible solution, and that is the proof that your problem is feasible.
Now, in finding the global optimum instead of a local optimum, this is a little bit harder. One way to find out is to check if your problem is convex. If it is, it means that there will only be one local optimum, and that this local optimum is the global optimum. This can be done mathematically. See https://math.stackexchange.com/a/1707213/470821 and http://www.princeton.edu/~amirali/Public/Teaching/ORF523/S16/ORF523_S16_Lec7_gh.pdf from a quick Google search). If you found that your problem is not convex, then you can try to prove that there are few local optimums and that they can be found easily with good starting points. Finally, if this can't be done, you should consider more advanced techniques, all with their pros and cons. For example, you can try to generate a set of starting solutions to make sure that you cover the whole feasible domain of your problem. Another one would be to use meta-heuristics methods to help you find a better starting solution.
Also, I am sure that Ipopt have some tools to help tackling this problem of finding a good starting solution that improves the resulting local optimum.

About autograd in pyorch, Adding new user-defined layers, how should I make its parameters update?

everyone !
My demand is a optical-flow-generating problem. I have two raw images and a optical flow data as ground truth, now my algorithm is to generate optical flow using raw images, and the euclidean distance between generating optical flow and ground truth could be defined as a loss value, so it can implement a backpropagation to update parameters.
I take it as a regression problem, and I have to ideas now:
I can set every parameters as (required_grad = true), and compute a loss, then I can loss.backward() to acquire the gradient, but I don’t know how to add these parameters in optimizer to update those.
I write my algorithm as a model. If I design a “custom” model, I can initilize several layers such as nn.Con2d(), nn.Linear() in def init() and I can update parameters in methods like (torch.optim.Adam(model.parameters())), but if I define new layers by myself, how should I add this layer’s parameters in updating parameter collection???
This problem has confused me several days. Are there any good methods to update user-defined parameters? I would be very grateful if you could give me some advice!
Tensor values have their gradients calculated if they
Have requires_grad == True
Are used to compute some value (usually loss) on which you call .backward().
The gradients will then be accumulated in their .grad parameter. You can manually use them in order to perform arbitrary computation (including optimization). The predefined optimizers accept an iterable of parameters and model.parameters() does just that - it returns an iterable of parameters. If you have some custom "free-floating" parameters you can pass them as
my_params = [my_param_1, my_param_2]
optim = torch.optim.Adam(my_params)
and you can also merge them with the other parameter iterables like below:
model_params = list(model.parameters())
my_params = [my_param_1, my_param_2]
optim = torch.optim.Adam(model_params + my_params)
In practice however, you can usually structure your code to avoid that. There's the nn.Parameter class which wraps tensors. All subclasses of nn.Module have their __setattr__ overridden so that whenever you assign an instance of nn.Parameter as its property, it will become a part of Module's .parameters() iterable. In other words
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.my_param_1 = nn.Parameter(torch.tensor(...))
self.my_param_2 = nn.Parameter(torch.tensor(...))
will allow you to write
module = MyModule()
optim = torch.optim.Adam(module.parameters())
and have the optim update module.my_param_1 and module.my_param_2. This is the preferred way to go, since it helps keep your code more structured
You won't have to manually include all your parameters when creating the optimizer
You can call module.zero_grad() and zero out the gradient on all its children nn.Parameters.
You can call methods such as module.cuda() or module.double() which, again, work on all children nn.Parameters instead of requiring to manually iterate through them.

userWarning pymc3 : What does reparameterize mean?

I built a pymc3 model using the DensityDist distribution. I have four parameters out of which 3 use Metropolis and one uses NUTS (this is automatically chosen by the pymc3). However, I get two different UserWarnings
1.Chain 0 contains number of diverging samples after tuning. If increasing target_accept does not help try to reparameterize.
MAy I know what does reparameterize here mean?
2. The acceptance probability in chain 0 does not match the target. It is , but should be close to 0.8. Try to increase the number of tuning steps.
Digging through a few examples I used 'random_seed', 'discard_tuned_samples', 'step = pm.NUTS(target_accept=0.95)' and so on and got rid of these user warnings. But I couldn't find details of how these parameter values are being decided. I am sure this might have been discussed in various context but I am unable to find solid documentation for this. I was doing a trial and error method as below.
with patten_study:
#SEED = 61290425 #51290425
step = pm.NUTS(target_accept=0.95)
trace = sample(step = step)#4000,tune = 10000,step =step,discard_tuned_samples=False)#,random_seed=SEED)
I need to run these on different datasets. Hence I am struggling to fix these parameter values for each dataset I am using. Is there any way where I give these values or find the outcome (if there are any user warnings and then try other values) and run it in a loop?
Pardon me if I am asking something stupid!
In this context, re-parametrization basically is finding a different but equivalent model that it is easier to compute. There are many things you can do depending on the details of your model:
Instead of using a Uniform distribution you can use a Normal distribution with a large variance.
Changing from a centered-hierarchical model to a
non-centered
one.
Replacing a Gaussian with a Student-T
Model a discrete variable as a continuous
Marginalize variables like in this example
whether these changes make sense or not is something that you should decide, based on your knowledge of the model and problem.

Random variable created with scipy.stats and multiprocessing : Pickle error

I'm no king in python, and recently got in trouble with a modification I made in my code. My algorithm is basically multiple uses of stochastic gradient algorithm and thus needs random variables.
I wanted my code to handle custom random variables and probability distribution. To do so, I modified my code and now I use scipy.stats to draw samples of custom random variables. Basically, I create a random variable with an imposed probability density or a cumulative density, and then draw samples thanks to the inverse function of the cumulative distribution function and some uniform random variable between [0,1].
To make it simple the algorithm runs multiple optimization from different starting point using stochastic gradient algorithm, and thus can be parallelized since the starting points are independent.
Problem is that the random variable created this way can't be pickled
PicklingError: Can't pickle : attribute lookup builtin.instancemethod failed
I don't get the subtility of pickling problems for now, so if you guys can help me solve this following simple illustration of the problem :
RV = scipy.stats.norm();
def Draw(rv,N):
return rv.ppf(np.random.random(N))
pDraw = partial(Draw,RV);
PM = multiprocessing.pool(Processes = 2);
L = PM.map(pDraw,range(1,5));
I've heard of pathos library that do not use the same serialization algorithm (dill), but I would like to avoid this solution (if it is a solution) as it is not included in my python distribution at work... making it install will take a lot of time.

model.remove_constraint() performance

I'm working with CPLEX/docplex solving an LP problem that has a lot of infeasible constraints, most of the issues in feasibility come from the automated formulation of the model, and its hard to detect a priory the conflicts between constraints.
using the docplex functions ConflictRefiner().refine_conflict(model) im able to found, at least, one set of constraints in conflict.
The problem is that, in order to found all the sets of constraints in conflict, I have to remove some of the constraints in conflict using the function model.remove_constraint(constraint.name) and that function takes a long time to execute.
Edit the timings for 135.000 constraints are:
model.remove_constraint(constraint.name)
time= 124 sec
model.remove_constraint(constraint.element)
time= 126 sec
¿Is there a way to remove a constraint faster than with model.remove_constraint(str_name_constraint)?¿is there a way to get all the sets in conflict without having to remove/refine_conflict() for each set?¿is there a way to use hierarchy in constraints in order to avoid conflicts between constraints?
(last question its a little out of topic, but its related with the original problem)
thanks in advance!
finally I used a workaround,
I didn't use mdl.remove_constraint(). to all the constraints i added a priority, and then I used the relaxer library provided by [docplex][1]. I couldn't found any example in the docs (or anywhere else) of the use of the relaxer, so i made one on my own (really simple to understand). The relaxer library is a really powerful tool, and its way much more easier to use rather than making all the relaxations by hand, especially when you have to deal with hierarchies in the constraints.
Example:
from docplex.mp.model import Model
import docplex
# we create a simple model
mdl = Model("relax_model")
x1=mdl.continuous_var(name='X1', lb=0)
x2=mdl.continuous_var(name='X2', lb=0)
# add conflict constraints
c1=mdl.add_constraint(x1<=10,'c1_low')
c2=mdl.add_constraint(x1<=5,'c2_medium')
c3=mdl.add_constraint(x1>=400,'c3_high')
c4=mdl.add_constraint(x2>=1,'c4_low')
mdl.minimize(x1+x2)
mdl.solve()
print mdl.report()
print mdl.get_solve_status() #infeasible model
print
print 'relaxation begin'
from docplex.mp.relaxer import Relaxer
rx = Relaxer(prioritizer='match')
rx.relax(mdl,relax_mode= docplex.mp.relaxer.RelaxationMode.OptInf)
print 'number_of_relaxations= ' + str(rx.number_of_relaxations)
print rx.relaxations()
print mdl.report()
print mdl.get_solve_status()
print mdl.solution
I know that this isn't "the solution" for the model.remove_constraint() performance problem, but it fits well when you need to avoid it.