How to apply constraint on a couple of matrix values in scipy? - python

I have a matrix A of size m x m
and while using scipy, I need to define the inequality constraint in python for items of this matrix that verifies:
.
I managed to define the bounds between -1 and 1 :
in python as follows:
# -1 <= a[i][j] <= 1
bnds_a_s_t = [(-1,1) for _ in range(np.size(A))]
But I don't know how to define the inequality constraint. Should I use a for loop with 2 pointers and add them to a list of inequality constraints? In this case, what should I return from the inequality function? In the equations, M is a list of size m.

If you want to use it in a nonlinear programming you can use have a matrix that is constrained by construction, starting with an arbitrary matrix A, you can use, for instance
def constrained(A):
return np.tanh(A - A.T);
The matrix a = A - A.T will ensure that a[t,s] + a[s,t] = 0 the tanh is such that tanh(-x) = -tanh(x), so the identity will be preserved for a = tanh(A - A.T), furthermore -1 <= tanh(x) <= 1. So constrained(A) gives what you need.

Related

Set bounds as constraints in cplex python

I have a large mip problem (~50 constraints, 25k variables) of this form:
min c x, s.t. Ax = b, Gx <= h
I have built the vectores c,b,h and dicts A and G that represents the matrices (they are sparse matrices). The last rows of the matrix G are the bounds of the variables as constraints. There are binary, positive continuous and continuous variables in the model.
I used the cplex library to solve it, but for some reason the solution that I get when I set the bounds as constraints in the matrix G is not the same solution that I get when I set the bounds in the variables to the problem. Here is the pseudocode of what I am doing:
import cplex
class MyModel():
...
def set_bounds_as_constraints(self):
for col,_ in enumerate(self.c):
if self.lbs[col] == self.ubs[col]: # lbs[col] = var = ubs[col]
self.A.update((row_A,col):1)
self.b.append(self.lbs[col])
else: # lbs[col] <= var <= ubs[col]
self.G.update((row,col):-1)
self.b.append(-1*self.lbs[col]) # -var <= -lbs[col] == lbs[col] <= var
self.G.update((row_G,col):1) # var <= ubs[col]
self.h.append(self.ubs[col])
...
def set_problem(self):
problem = cplex.Cplex()
problem.set_problem_type(problem.problem_type.LP)
problem.objective.set_sense(problem.objective.sense.minimize)
# first method, this way the solver gives bad solutions
problem.variables.add(obj=list(self.c.values()), types=self.types_vars, names=names_vars)
# second method, this way the solver seems to works fine
problem.variables.add(obj=list(self.c.values()), types=self.types_vars, lb=self.lb_vars,
ub=self.ub_vars, names=names_vars)
problem.linear_constraints.add ...
The first method correspond to set the bounds as constraints in the matrix G, and the second to have these constraints as lower and upper bounds when adding the variables to the model.
If I export the .lp files of both problem, the difference between them is that the second method add the bounds in the bottom of the file and the first creates constraints that the second method does not has, but I can see that the constraints that represents the bounds are correct. What am I doing wrong?

Nonlinear optimization with scipy.optimize.minimize using matrices and vectors as decision variables

I have a nonlinear optimization problem which makes use of 3 decision variables, one of these variables is a single number (t), one is a vector with index i (S_i) and one is a matrix (Q_i,j) with indices i and j. I'm currently trying to use scipy.optimize.minimize to model and solve my problem but I can't get it to work.
Because of the multiple indices, constraints must often hold for all values of some index. My actual model is very large so let's consider this example which we assume is nonlinear:
Decision variable: Q_i,j
Objective:
minimize Sum over all i and all j, Q_i,j
Constraint_1:
Q_i,j / 2 >= 10 for all i, for all j
Current code I try to use:
from scipy.optimize import minimize
import numpy as np
I = 5
J = 5
x0 = np.zeros(I*J)
def obj(Q_ij):
Q_ijcp = np.reshape(Q_ijcp,(I,J))
return sum(Q_ij[i,j] for i in range(I) for j in range(J))
def cons_1(Q_ij):
Q_ijcp = np.reshape(Q_ijcp,(I,J))
return (Q_ij[i,j] / 2 - 10 for i in range(I) for j in range(J))
b = (0, 100)
bounds = []
for i in range(I):
for j in range(J):
bounds.append(b)
constraint1 = {"type": "ineq", "fun": cons_1}
constraints = [constraint1]
solution = minimize(obj, x0=x0, bounds=bounds, constraints=constraints, method='SLSQP')
Based on the user guide I found that for each constraint one must make a definition such that it can be entered into the solver which I try above but it does not work, how can I model this such that I don't have to make a definition for each possible i and j value in Constraint_1? (Such that I don't end up with 5 * 5 constraints as I=J=5)
Or are there any other packages with good documentation/examples which are easier to use in the case of using vectors and matrices with constraints for their indices?
Or are there any other packages with good documentation/examples which are easier to use in the case of using vectors and matrices with constraints for their indices?
Below is an example problem with Python gekko with solver IPOPT to solve a similar nonlinear optimization problem.
from gekko import GEKKO
m = GEKKO()
n = 5
Q = m.Array(m.Var,(n,n),lb=10,ub=100)
t = m.Var(lb=0,ub=1)
S = m.Array(m.Var,n,lb=0,ub=1)
# add constraint t*Q*S > 100
QS = Q#S # dot product
m.Equations([t*QS[i]>100 for i in range(n)])
m.Minimize(m.sum([Q[i,j] for i in range(n)
for j in range(n)]))
m.options.SOLVER=3
m.solve()
print(t.value[0])
print(S)
print(Q)
Optimizers APOPT and IPOPT can handle 100,000+ variables. Gekko Documentation and examples are available.

Using a forloop to solve coupled differential equations in python

I am trying to solve a set of differential equations, but I have been having difficulty making this work. My differential equations contain an "i" subscript that represents numbers from 1 to n. I tried implementing a forloop as follows, but I have been getting this index error (the error message is below). I have tried changing the initial conditions (y0) and other values, but nothing seems to work. In this code, I am using solve_ivp. The code is as follows:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.integrate import solve_ivp
def testmodel(t, y):
X = y[0]
Y = y[1]
J = y[2]
Q = y[3]
a = 3
S = 0.4
K = 0.8
L = 2.3
n = 100
for i in range(1,n+1):
dXdt[i] = K**a+(Q[i]**a) - S*X[i]
dYdt[i] = (K*X[i])-(L*Y[i])
dJdt[i] = S*Y[i]-(K*Q[i])
dQdt[i] = K*X[i]/L+J[i]
return dXdt, dYdt, dJdt, dQdt
t_span= np.array([0, 120])
times = np.linspace(t_span[0], t_span[1], 1000)
y0 = 0,0,0,0
soln = solve_ivp(testmodel, t_span, y0, t_eval=times,
vectorized=True)
t = soln.t
X = soln.y[0]
Y = soln.y[1]
J = soln.y[2]
Q = soln.y[3]
plt.plot(t, X,linewidth=2, color='red')
plt.show()
The error I get is
IndexError Traceback (most recent call last)
<ipython-input-107-3a0cfa6e42ed> in testmodel(t, y)
15 n = 100
16 for i in range(1,n+1):
--> 17 dXdt[i] = K**a+(Q[i]**a) - S*X[i]
IndexError: index 1 is out of bounds for axis 0 with size 1
I have scattered the web for a solution to this, but I have been unable to apply any solution to this problem. I am not sure what I am doing wrong and what to actually change.
I have tried to remove the "vectorized=True" argument, but then I get an error that states I cannot index scalar variables. This is confusing because I do not think these values should be scalar. How do I resolve this problem, my ultimate goal is to plot these differential equations. Thank you in advance.
It is nice that you provide the standard solver with a vectorized ODE function for multi-point evalutions. But the default method is the explicit RK45, and explicit methods do not use Jacobi matrices. So there is no need for multi-point evaluations for difference quotients for the partial derivatives.
In essence, the coordinate arrays always have size 1, as the evaluation is at a single point, so for instance Q is an array of length 1, the only valid index is 0. Remember, in all "true" programming languages, array indices start at 0. It is only some CAS script languages that use the "more mathematical" 1 as index start. (Setting n=100 and ignoring the length of the arrays provided by the solver is wrong as well.)
You can avoid all that and shorten your routine by taking into account that the standard arithmetic operations are applied element-wise for numpy arrays, so
def testmodel(t, y):
X,Y,J,Q = y
a = 3; S = 0.4; K = 0.8; L = 2.3
dXdt = K**a + Q**a - S*X
dYdt = K*X - L*Y
dJdt = S*Y - K*Q
dQdt = K*X/L + J
return dXdt, dYdt, dJdt, dQdt
Modifying your code for multiple compartments with the same dynamic
You need to pass the solver a flat vector of the state. The first design decision is how the compartments and their components are arranged in the flat vector. One variant that is most compatible with the existing code is to cluster the same components together. Then in the ODE function the first operation is to separate out these clusters.
X,Y,J,Q = y.reshape([4,-1])
This splits the input vector into 4 pieces of equal length. At the end you need to reverse this split so that the derivatives are again in a flat vector.
return np.concatenate([dXdt, dYdt, dJdt, dQdt])
Everything else remains the same. Apart from the initial vector, which needs to have 4 segments of length N containing the data for the compartments. Here that could just be
y0 = np.zeros(4*N)
If the initial data is from any other source, and given in records per compartment, you might have to transpose the resulting array before flattening it.
Note that this construction is not vectorized, so leave that option unset in its default False.
For uniform interaction patterns like in a circle I recommend the use of numpy.roll to continue to avoid the use of explicit loops. For an interaction pattern that looks like a network one can use connectivity matrices and masks like in Using python built-in functions for coupled ODEs

Python Linear Programming, Constrain Input to Zero or Range

In an Linear Program I am minimizing the distance between weighted input vectors and a target vector. I used Scipyto compute values for the weights I need. Currently they are between zero and one, but I'd like them to be zero if they are smaller than .2 for example, so x_i should be 0 or [.2; 1]. I was pointed to mixed integer linear programming but I still can't find any approach for my problem. How can I fix this?
tldr: i want to use (0,0) or (.3,1) as bounds for each x, how do i implement this?
Here is my SciPy code:
# minimize the distance between weighted input vectors and a target vector
def milp_objective_function(weights):
scaled_matrix = input_matrix * weights[:, np.newaxis] # scale input_matrix columns by weights
sum_vector = sum(scaled_matrix) # sum weighted_input_matrix columns
difference_vector = sum_vector - target_vector
return np.sqrt(difference_vector.dot(difference_vector)) # return the distance between the sum_vector and the target_vector
# sum of weights should equal 100%
def milp_constraint(weights):
return sum(weights) - 1
def main():
# bounds should be 0 or [.2; 1] -> mixed integer linear programming?
weight_bounds = tuple([(0, 1) for i in input_matrix])
# random guess, will implement later
initial_guess = milp_guess_weights()
constraint_obj = {'type': 'eq', 'fun': milp_constraint}
result = minimize(milp_objective_function, x0=initial_guess, bounds=weight_bounds, constraints=constraint_obj)
Variables that are in {0} ∪ [L,U] are called semi-continuous variables. Advanced MIP solvers have built-in support for these types of variables.
Note that SciPy does not have a MIP solver at all.
I also want to note that if your MIP solver does not support semi-continuous variables you can simulate them with binary variables:
L ⋅ δ(i) ≤ x(i) ≤ U ⋅ δ(i)
δ(i) ∈ {0,1}

Minimize the max value in Gurobi optimaztion

I am developing a model to solve a MIP problem using gurobi and python. The problem involves travel times over a set of predefined routes. One of the objective functions I am trying to realize is to minimize the maximum travel time for the selected routes. A mathematical representation of this is:
min f = max(Dij * Zij)
where D is the travel time for each route ij and Z is the assignment variable indicating whether route ij is part of the solution, so that if the route is not selected then the expression evaluates to 0. What is the best way to model this in Gurobi for python?
Here's how you can set up a min max constraint in MIP/Gurobi.
Idea: First, create a new variable called max_distance. This is what the MIP will try to minimize.
Now add constraints, one for each (i,j) combination, such that:
dist[i][j] * Z[i][j] <= max_distance
The above will take care of pushing max_distance to be at least as large as the largest Dij. And the objective function will make max_distance as small as possible.
To make the code that follows work, you have to do two things.
Add in your actual constraints that 'select' the preferred set of Zij
Replace my random values with your actual distances.
Gurobi (Python) Code for MinMax
Here's how you'd approach it in Gurobi (Python). I don't have Gurobi installed, so this hasn't been verified. It is to illustrate the idea of min max.
import sys
import math
import random
import itertools
from gurobipy import *
#Create 10 points (nodes i and j) with random values.
# replace this with your distances.
N=10
random.seed(1)
points = [(random.randint(0,100),random.randint(0,100)) for i in range(n)]
dist = {(i,j) :
math.sqrt(sum((points[i][k]-points[j][k])**2 for k in range(2)))
for i in range(n) for j in range(i)}
m = Model()
# minimize 1 * maxDistvar
mdvar = m.addVar(lb=0.0, obj=1.0, GRB.CONTINUOUS, "maxDistvar")
# Create the Zij variables
vars = tupledict()
for i,j in dist.keys():
vars[i,j] = m.addVar(vtype=GRB.BINARY,
name='z[%d,%d]'%(i,j))
#set up limit max distance constraints
# Maxdistvar is greater than or equal to the largest dist[i, j]
for i in range(N):
for j in range(i):
m.addConstr(vars[i,j]*dist[i, j] <= mdvar, 'maxDist[%d,%d]'%(i,j))
# Also, add your constraints that 'select' \
# certain Zij to be 0 or 1 based on other criteria
# These will decide if Zij is part of your solution.
# Solve
m.optimize()
And print out the selected Zij's. Hope that helps.

Categories