I am currently trying to solve a Mixed Integer Non Linear Problem with Gekko using its Branch&Bound implementation coupled with its warm-start method to speed up and improve the convergence process compared to vanilla branch& bound.
The algorithm finds a solution after a short amount of time. Nevertheless, it hurts the constraint, which I might have defined wrongly: I have a gekko array-variable X and need another gekko array-variable "indices_open" that saves every index of x where x == 1. This "indices_open" goes into another self-defined function which is expecting "indices_open" as an numpy array and does not accept a list or gekko-array of gekko-intermediate variables. The self-defined function returns a numpy array. This final array shall be used in m.Equations and I therefore cast it to a gekko variable array.
Needless to say, something went wrong and the current solution hurts the inequality constraint, while the equality constraint is met. While analyzing the result, I came to the conclusion that "indices_open" seems not to have updated in each iteration.
In the following my try so far:
m = GEKKO()
m.options.SOLVER = 1 # APOPT is an MINLP solver
# optional solver settings with APOPT
m.solver_options = ['minlp_maximum_iterations 500', \
# minlp iterations with integer solution
'minlp_max_iter_with_int_sol 10', \
# treat minlp as nlp
'minlp_as_nlp 0', \
# nlp sub-problem max iterations
'nlp_maximum_iterations 50', \
# 1 = depth first, 2 = breadth first
'minlp_branch_method 1', \
# maximum deviation from whole number
'minlp_integer_tol 0.05', \
# covergence tolerance
'minlp_gap_tol 0.01']
#Declare x
x = m.Array(m.Var,(65),lb=0,ub=1,integer=True)
for i, xi in enumerate(x[0:65]):
xi.value = np.random.choice(np.arange(0, 2), 1, p=[0.4, 0.6])[0]
#constr
m = ineq_constraint_new(x, m)
m = eq_constraint_new(x, m)
#target
m = objective(x,m)
#STArt
start_time = time.time()
#m.solve(disp=False)
m.solve()
print('Objective: ' + x)
print('Objective: ' + str(m.options.objfcnval))
# save x
m.x = [x[j].value[0] for j in range(65)]
def eq_constraint_new(x, m):
mask = np.isin(list_unique, specific_value)
indices_fixed = np.nonzero(mask)[0]
m.Equations([x[j] == 1 for j in indices_fixed])
return m
def ineq_constraint_new(x, m):
indices_open = [j for j in range(65) if x[j].value == 1]
# DOes not work
#indices_open_banks = [m.Intermediate(j) for j in range(65) if x[j].value == 1]
array_perc, _, _,_ = self_defined_f(indices_open, some_value)
#convert to gekko variables
gekko_vec_perc_upper_bound = m.Array(m.Var, (65))
for i, xi in enumerate(gekko_vec_perc_upper_bound[0:65]):
xi.value = some_array[i]
gekko_arr_perc = m.Array(m.Var, (65))
for i, xi in enumerate(gekko_arr_perc[0:65]):
xi.value = arr_perc[i]
diff = gekko_vec_perc_upper_bound - gekko_arr_perc
m.Equations([diff[j] >= 0 for j in range(65)])
return m
def objective(x,m):
indices_open = [j for j in range(65) if x[j].value == 1]
_, arr_2, arr_3, arr_4 = self_defined_f(indices_open,some_value )
# intermediates for objective
res_dist = [None] * self.ds.n_banks
res_wand = [None] * self.ds.n_banks
res_wand_er = [None] * self.ds.n_banks
x_closed = np.array([1]*len(x)) - x
for j in range(self.ds.n_banks):
res_dist[j] = m.Intermediate(arr_2[j] * some_factor )
res_wand[j] = m.Intermediate(arr_3[j] * some_factor)
res_wand_er[j] = m.Intermediate(arr_4[j] * some_factor)
res_sach = some_factor * (some_vector * x_closed)
# Will be added together
m.Minimize(sum(res_dist))
m.Minimize(sum(res_wand))
m.Minimize(sum(res_wand_er))
m.Maximize(sum(res_sach))
return m
There is an undefined function _, arr_2, arr_3, arr_4 = self_defined_f(indices_open,some_value ) that prevents the code from running. From a quick scan of the code, an expression like:
indices_open = [j for j in range(65) if x[j].value == 1]
is not allowed because gekko requires that the equations are all defined before the m.solve() command. A callback to a function in Python is not allowed. Instead, binary variables should be used to turn something On or Off in the optimization problem. This can be an equation such as binary variable b:
b = m.Var(lb=0,ub=1,integer=True)
m.Equation(x*(1-b)<=0)
m.Equation(x*b>=0)
This makes the value of b equal to 0 if x is less than zero and b equal to 1 if x if greater than zero. There is a tutorial on if3() functions in the APMonitor documentation that may also be useful.
Related
currently running into a problem solving this.
The objective of the exercise given is to find a polynom of certian degree (the degree is given) from a dataset of points (that can be noist) and to best fit it using least sqaure method.
I don't understand the steps that lead to solving the linear equations?
what are the steps or should anyone provide such a python program that lead to the matrix that I put as an argument in my decomposition program?
Note:I have a python program for cubic splines ,LU decomposition/Guassian decomposition.
Thanks.
I tried to apply guassin / LU decomposition straight away on the dataset but I understand there are more steps to the solution...
I donwt understand how cubic splines add to the mix either..
Edit:
guassian elimintaion :
import numpy as np
import math
def swapRows(v,i,j):
if len(v.shape) == 1:
v[i],v[j] = v[j],v[i]
else:
v[[i,j],:] = v[[j,i],:]
def swapCols(v,i,j):
v[:,[i,j]] = v[:,[j,i]]
def gaussPivot(a,b,tol=1.0e-12):
n = len(b)
# Set up scale factors
s = np.zeros(n)
for i in range(n):
s[i] = max(np.abs(a[i,:]))
for k in range(0,n-1):
# Row interchange, if needed
p = np.argmax(np.abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swapRows(b,k,p)
swapRows(s,k,p)
swapRows(a,k,p)
# Elimination
for i in range(k+1,n):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
b[i] = b[i] - lam*b[k]
if abs(a[n-1,n-1]) < tol: error.err('Matrix is singular')
# Back substitution
b[n-1] = b[n-1]/a[n-1,n-1]
for k in range(n-2,-1,-1):
b[k] = (b[k] - np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]
return b
def polyFit(xData,yData,m):
a = np.zeros((m+1,m+1))
b = np.zeros(m+1)
s = np.zeros(2*m+1)
for i in range(len(xData)):
temp = yData[i]
for j in range(m+1):
b[j] = b[j] + temp
temp = temp*xData[i]
temp = 1.0
for j in range(2*m+1):
s[j] = s[j] + temp
temp = temp*xData[i]
for i in range(m+1):
for j in range(m+1):
a[i,j] = s[i+j]
return gaussPivot(a,b)
degree = 10 # can be any degree
polyFit(xData,yData,degree)
I was under the impression the code above gets a dataset of points and a degree. The output should be coeefients of a polynom that fits those points but I have a grader that was provided by my proffesor , and after checking the grading the polynom that returns has a lrage error.
After that I tried the following LU decomposition instead:
import numpy as np
def swapRows(v,i,j):
if len(v.shape) == 1:
v[i],v[j] = v[j],v[i]
else:
v[[i,j],:] = v[[j,i],:]
def swapCols(v,i,j):
v[:,[i,j]] = v[:,[j,i]]
def LUdecomp(a,tol=1.0e-9):
n = len(a)
seq = np.array(range(n))
# Set up scale factors
s = np.zeros((n))
for i in range(n):
s[i] = max(abs(a[i,:]))
for k in range(0,n-1):
# Row interchange, if needed
p = np.argmax(np.abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swapRows(s,k,p)
swapRows(a,k,p)
swapRows(seq,k,p)
# Elimination
for i in range(k+1,n):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
a[i,k] = lam
return a,seq
def LUsolve(a,b,seq):
n = len(a)
# Rearrange constant vector; store it in [x]
x = b.copy()
for i in range(n):
x[i] = b[seq[i]]
# Solution
for k in range(1,n):
x[k] = x[k] - np.dot(a[k,0:k],x[0:k])
x[n-1] = x[n-1]/a[n-1,n-1]
for k in range(n-2,-1,-1):
x[k] = (x[k] - np.dot(a[k,k+1:n],x[k+1:n]))/a[k,k]
return x
the results were a bit better but nowhere near what it should be
Edit 2:
I tried the chebyshev method suggested in the comments and came up with:
import numpy as np
def chebyshev_transform(x, n):
"""
Transforms x-coordinates to Chebyshev coordinates
"""
return np.cos(n * np.arccos(x))
def chebyshev_design_matrix(x, n):
"""
Constructs the Chebyshev design matrix
"""
x_cheb = chebyshev_transform(x, n)
T = np.zeros((len(x), n+1))
T[:,0] = 1
T[:,1] = x_cheb
for i in range(2, n+1):
T[:,i] = 2 * x_cheb * T[:,i-1] - T[:,i-2]
return T
degree =10
f = lambda x: np.cos(X)
xdata = np.linspace(-1,1,num=100)
ydata = np.array([f(i) for i in xdata])
M = chebyshev_design_matrix(xdata,degree)
D_x ,D_y = np.linalg.qr(M)
D_x, seq = LUdecomp(D_x)
A = LUsolve(D_x,D_y,seq)
I can't use linalg.qr in my program , it was just for checking how it works.In addition , I didn't get the 'slow way' of the formula that were in the comment.
The program cant get an x point that is not between -1 and 1 , is there any way around it , any normalizition?
Thanks a lot.
Hints:
You are probably asked for an unsophisticated method. If the degree of the polynomial remains low, you can use the straightforward approach below. For the sake of the explanation, I'll use a cubic model.
Assume that you want to fit your data to this polynomial, by observing that it seems to follow a cubic behavior:
ax³ + bx² + cx + d ~ y
[All x and y should be understood with an index i which is omitted for notational convenience.]
If there are more than four data points, you get an overdetermined system of equations, usually with no solution. The trick is to consider the error on the individual equations, e = ax³ + bx² + cx + d - y, and to minimize the total error. As the error is a signed number, negative errors would make minimization impossible. Instead, we minimize the sum of squared errors. (The sum of absolute errors is another option but it unfortunately leads to a much harder problem.)
Min(a, b, c, d) Σ(ax³ + bx² + cx + d - y)²
As the unknown parameters are unconstrained, it suffices to look for a stationary point, i.e. cancel the gradient of the total error. By differentiation on the unknowns a, b, c and d, we obtain
2Σ(ax³x³ + bx²x³ + cxx³ + dx³ - yx³) = 0
2Σ(ax³x² + bx²x² + cxx² + dx² - yx²) = 0
2Σ(ax³x + bx²x + cxx + dx - yx ) = 0
2Σ(ax³ + bx² + cx + d - y ) = 0
As you can recognize, this is a square linear system of equations.
I am trying to solve an NLP with GEKKO, however I have a few problem while implementing the Python code. The model that I am trying to solve is pretty trivial, I am trying to find the optimal point that has minimum loss function value in a 3D convex set.
def calculateLossFunction(h, x, y, lmbd, n):
sum = 0
x_star = np.dot(np.transpose(lmbd), x)
y_star = np.dot(np.transpose(lmbd), y)
for i in range(n):
RNJ = math.sqrt((x_star - x[i]) ** 2 + (y_star - y[i]) ** 2)
P = 1 / (math.degrees(math.atan(h[i] / RNJ)))
sum += A * P + B
return sum
This is the objective function for my problem and I am using this as follows
m = GEKKO(remote=True)
eq = m.Param()
H = [500, 1500, 2500]
locations = np.array([[1, 2],
[2, 3],
[3, 1]])
XN = locations[:, 0]
YN = locations[:, 1]
n = len(locations)
lambdas = m.Array(m.Var,n,lb=0, ub = 1, value = 0)
lambdas[0].value = 1
m.Minimize(calculateLossFunction(H, XN, YN, lambdas, n))
m.Equation(sum(lambdas) == 1)
m.solve(disp=True) # solve on public server
#Results
print('')
print('Results')
print('x1: ' + str(lambdas[0].value))
print('x2: ' + str(lambdas[1].value))
print('x3: ' + str(lambdas[2].value))
The thing is, although I've checked similar problems that are raised in Stack Overflow and tried to mimic the recommended solutions, at this point seems like I cannot figure out what is wrong because above code gives the following error.
Traceback (most recent call last):
m.Minimize(calculateLossFunction(H, XN, YN, lambdas, n))
File "C:\Users\admin\PycharmProjects\nonlinear.py", line 13, in calculateLossFunction
RNJ = math.sqrt((x_star - x[i]) ** 2 + (y_star - y[i]) ** 2)
TypeError: must be real number, not GK_Operators
I've also read the documentation but couldn't find any solution.
Thanks in advance for your answers.
Use the gekko functions m.sqrt() and m.atan() instead of math.sqrt() and math.atan(). The TypeError: must be real number, not GK_Operators is from the math function. There is no math.degrees() equivalent in gekko, so use 360.0/(2.0*np.pi) for the conversion. Gekko uses gradient-based optimizers that require overloading of the operators and functions for automatic differentiation to provide exact 1st and 2nd derivatives of constraints and objectives. Some functions are compatible such as np.dot() while others do not return a symbolic solution, such as math.sqrt(). Here is a complete problem that solves successfully:
from gekko import GEKKO
import numpy as np
A = 1.0; B=0.0
def calculateLossFunction(h, x, y, lmbd, n):
sum = 0
x_star = np.dot(np.transpose(lmbd), x)
y_star = np.dot(np.transpose(lmbd), y)
for i in range(n):
RNJ = m.sqrt((x_star - x[i]) ** 2 + (y_star - y[i]) ** 2)
P = 1 / (360.0*(m.atan(h[i] / RNJ)/(2.0*np.pi)))
sum += A * P + B
return sum
m = GEKKO(remote=True)
eq = m.Param()
H = [500, 1500, 2500]
locations = np.array([[1, 2],
[2, 3],
[3, 1]])
XN = locations[:, 0]
YN = locations[:, 1]
n = len(locations)
lambdas = m.Array(m.Var,n,lb=0, ub = 1, value = 0)
lambdas[0].value = 1
m.Minimize(calculateLossFunction(H, XN, YN, lambdas, n))
m.Equation(sum(lambdas) == 1)
m.solve(disp=True) # solve on public server
print('Results')
print('x1: ' + str(lambdas[0].value))
print('x2: ' + str(lambdas[1].value))
print('x3: ' + str(lambdas[2].value))
Solution with sample A=1.0 and B=0.0 values:
Results
x1: [0.99999702144]
x2: [1.9787728836e-06]
x3: [9.9978717975e-07]
and the solver output:
Number of Iterations....: 113
(scaled) (unscaled)
Objective...............: 3.3346336759950239e-02 3.3346336759950239e-02
Dual infeasibility......: 8.4348140936638533e-07 8.4348140936638533e-07
Constraint violation....: 0.0000000000000000e+00 0.0000000000000000e+00
Complementarity.........: 1.0000010522025397e-11 1.0000010522025397e-11
Overall NLP error.......: 8.4348140936638533e-07 8.4348140936638533e-07
Number of objective function evaluations = 1237
Number of objective gradient evaluations = 114
Number of equality constraint evaluations = 1237
Number of inequality constraint evaluations = 0
Number of equality constraint Jacobian evaluations = 114
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 113
Total CPU secs in IPOPT (w/o function evaluations) = 0.067
Total CPU secs in NLP function evaluations = 0.034
EXIT: Optimal Solution Found.
The solution was found.
The final value of the objective function is 3.334633675995024E-002
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 0.131699999998091 sec
Objective : 3.334633675995024E-002
Successful solution
---------------------------------------------------
Trigonometric functions sometimes need constraints on the variables to ensure that a NaN value is not returned or to make a solution unique (such as cos(np.pi) and cos(-np.pi).
Well what I was trying to do was to model the following using scipy.optimize.minimize.
What I'm trying to optimize is this function with its constraints:
Here variable V is a list of variables, list's length is equal to the size of Omega.
What I did so far is the following:
import numpy as np
from scipy.linalg import norm
from scipy.optimize import minimize
# set of concepts
M = ['linear algebra','seq2seq', 'artificial neural network','pointer networks']
#subchapters
S1=['linear algebra', 'differential calculus','backpropagation']
S2=['linear algebra','seq2seq', 'artificial neural network']
S3=['linear algebra','seq2seq', 'artificial neural network','pointer networks']
#vector representation of the subchapter in the concept space
x=[[1,1,0,0],
[1,1,1,0],
[1,1,1,1]]
# set of prerequisite relations among subchapters (entered manually for testing)
Omega = [(1, 2),(2,3),(1,3)]
# number of concepts
m = len(M)
# define of theta and lambda constants (manual)
theta = 2
lamda = 1
# matrix A is a m x m matrix , where m is the number of concepts
# it represents the prerequisite relations among concepts
# A is generated randomly
np.random.seed(43)
#A = np.zeros((m,m), dtype = int)
A = np.random.randint(2, size=(m,m))
# define the slack variable V as an array of size equal to len(Omega)
V = np.empty(len(Omega), dtype=float)
bnds=[]
# bounds -1 and 1 , create the array
# -1 <= a[i][j] <= 1
bnds_a_s_t = [bnds.append((-1, 1)) for _ in range(np.size(A))]
# bounds for the slack variable V, V is positive
bnds_V_i_j = [bnds.append((0,np.inf)) for _ in range(np.size(V))]
#constraints
cons=[]
#equality constraint
#a[s][t] + a[t][s] = 0
def equality_constraint(X):
A_no_flatten=X[:len(X)-len(Omega)]
#reconstruct matrix A
A=np.reshape(A_no_flatten,(m,m))
for s in range(m):
for t in range(m):
r=A[s][t]+A[t][s]
#r=0 constraint
con = {'type': 'eq', 'fun': lambda X: r}
cons.append(con)
# inequality constraint
#x[i].T # (C[i][j] * A) # x[j]
def inequality_constraint(X,x):
for couple in Omega:
# get the i and j
i = couple[0]
j = couple[1]
#initialize C to 1s
C = np.full((m,m), 1, dtype = int)
# take all elements from X except last len(Omega) elements
A_no_flatten=X[:len(X)-len(Omega)]
# reconstruct list V
V=X[-len(Omega):]
#index for V
f=0
#reconstruct matrix A
A=np.reshape(A_no_flatten,(m,m))
#construct C[i][j]
for s in range(m):
for t in range(m):
if x[i][t]>0 or x[j][s]>0:
C[s][t]=0
else:
C[s][t]=1
first= x[i].T
second = C*A
third = x[j]
first_sec = first#second
res=first_sec#third
ineq_con = {'type': 'ineq', 'fun': lambda X: res -theta +V[f]}
f+=1
cons.append(ineq_con)
# arguments passed to the function
# here we pass x matrix
# arguments are passed and used in constraints and in the objective function
# the objective function will minimize A and V which are matrix A and slack variable V
arguments=(x,)
# objective function
def objective(X, x):
A_no_flatten=X[:len(X)-len(Omega)]
# reconstruct list V
V=X[-len(Omega):]
#reconstruct matrix A
A=np.reshape(A_no_flatten,(m,m))
# sum of square V
sum_square=0.0
for it in V:
sum_square+=it**2
# sum of square V * lambda
sum_square_lambda=sum_square*lamda
return norm(A, 1) + sum_square_lambda
# list of variables to pass to the objective function
#pass the x0.flatten() which is the A + V combined, and then when in objective function we recreate them
# the first one A is all except the last s items where s is the size of V
# and then V is the rest
B = A.flatten()
p0 = np.append(B,V)
# scipy minimize
sol = minimize(objective, x0 = p0, args=arguments, bounds=bnds, constraints=cons)
print(sol.x)
What I get is the following:
[-7.73458566e-010 0.00000000e+000 4.99999997e-001 1.00000000e+000
1.00000000e+000 0.00000000e+000 -5.00000003e-001 1.00000000e+000
1.00000000e+000 1.00000000e+000 4.99999997e-001 -7.73458566e-010
-7.73458566e-010 0.00000000e+000 4.99999997e-001 -7.73458566e-010
6.01347002e-154 1.07176259e-311 0.00000000e+000]
Which doesn't respect the constraints and is not what I expected
What I don't know is that is it correct to add constraints like that, because I don't seem to call the constraints function, and I need to add them in a loop, and each function depends on X which is the list to minimize.
When I print the cons array it is empty and I know, but I didn't find another way to add the constraint a[s][t]+a[t][s]=0 and the other one, I don't know if my approach is correct, thank you for your help in advance, much appreciated.
This isn't a complete answer, but it may get you started. As already mentioned in the comments, your list of constraints cons is empty when passed to the minimize method. So let's consider your first equality constraint. There are a few problems:
Each time you call the function equality_constraint, you'd append new constraints to your list cons.
Since you pass each constraint A[s][t] + A[t][s] == 0 as scalar function, it is quite cumbersome. Instead, you could use a single vector-valued function:
def constraint1(X):
A_no_flatten = X[:len(X)-len(Omega)]
A = np.reshape(A_no_flatten,(m,m))
return A.flatten() + A.T.flatten()
As the name indicates, the .flatten() method flattens the matrix to a vector and A.T is just the transpose of A. Now you can add this constraint:
cons = []
cons.append({'type': 'eq', 'fun': constraint1})
Proceed similarly for the other constraint.
I am tring to implement a facility location optimization model in Gurobi (Python interface). I have some difficulties to translate the model. The mathmatical model is shown below:
where dloc,floc are the (x,y) coordinates of the demand (customer) and facility (warehouse) locations. The dloc quantities are constants (i.e.50), opposed to floc which are decision variables: these are calculated by the solver. also, x,y coordinates are float numbers between 0 and 100.
One of the key issue is I dont know how to add the facility variable, the number of which can be any between 0 and n.
my codes so far:
from gurobipy import *
import numpy as np
import math
def distance(a, b):
dx = a[0] - b[0]
dy = a[1] - b[1]
return math.sqrt(dx ** 2 + dy ** 2)
customer = np.random.uniform(0,100,[50,2])
print(customer)
m = Model()
n = m.addVar(lb=0.0, ub=GRB.INFINITY,vtype=GRB.INTEGER) #number of candidate facilities
facility={}
for j in range(n):
facility[j] = m.addVar(vtype=GRB.BINARY, name="facility%d" % j) #certainly this is not correct, as an error is reported as 'Var' object cannot be interpreted as an integer
floc = ?
So I have tried another way by manually set a fixed number of candidate facility as an interim workaround:
from gurobipy import *
import numpy as np
import math
customer = np.random.uniform(0,100,[50,2])
print(customer)
m = Model()
###Variable
dc={}
x={}
y={}
assign={}
for j in range(10):
dc[j] = m.addVar(lb=0,ub=1,vtype=GRB.BINARY, name="DC%d" % j)
x[j]= m.addVar(lb=0, ub=100, vtype=GRB.CONTINUOUS, name="x%d")
y[j] = m.addVar(lb=0, ub=100, vtype=GRB.CONTINUOUS, name="y%d")
for i in range(len(customer)):
for j in range(len(dc)):
assign[(i,j)] = m.addVar(lb=0,ub=1,vtype=GRB.BINARY, name="Cu%d from DC%d" % (i,j))
###Constraint
for i in range(len(customer)):
for j in range(len(dc)):
m.addConstr(((customer[i][0] - x[j])*(customer[i][0] - x[j]) +\
(customer[i][1] - y[j])*(customer[i][1] - y[j])) <= 40*40 + 100*100*(1-assign[(i,j)]))
for i in range(len(customer)):
m.addConstr(quicksum(assign[(i,j)] for j in range(len(dc))) == 1)
for i in range(len(customer)):
for j in range(len(dc)):
m.addConstr(assign[(i, j)] <= dc[j])
n=0
for j in dc:
n=n+dc[j]
m.setObjective(n,GRB.MINIMIZE)
m.optimize()
print('\nOptimal Solution is: %g' % m.objVal)
for v in m.getVars():
print('%s %g' % (v.varName, v.x))
Anyone could demonstrate the translation of the model in Gurobi would be great help.
I see no problem in your definition for n. Nonetheless I rewrote your code to make it less verbose and easier to understand. First we create the given sets and constants:
from gurobipy import Model, GRB, quicksum
import numpy as np
m = Model()
demo_coords = np.random.uniform(0, 100, size=(50, 2)) # Just for demonstration
# Sets and Constants
demand = [f"i{k}" for k in range(1, 51)]
facilities = [ f"facility{k}" for k in range(1, 11) ]
dloc = {fac : demo_coords[i] for i, fac in enumerate(demand)}
maxdist = 40
M = 10e6
Note that dloc is a dictionary such that dloc[i] will give you the coordinates
for demand point i. Then dloc[i][0] is the x-coordinate and dloc[i][1] the
y-coordinate.
Now we can create the variables and store them in a gurobi tubledict:
# Variables
floc = m.addVars(facilities, 2, name="floc")
isopen = m.addVars(facilities, vtype=GRB.BINARY, name="isopen")
assign = m.addVars(demand, facilities, vtype=GRB.BINARY, name="assign")
n = m.addVar(vtype=GRB.INTEGER, name="n")
m.update()
Using m.addConstrs(), the constraints can be written as
# Constraints
m.addConstrs(((dloc[i][0] - floc[j, 0]) * (dloc[i][0] - floc[j, 0]) \
+ (dloc[i][1] - floc[j, 1])*(dloc[i][1] - floc[j, 1]) \
<= maxdist**2 + M * (1 - assign[i, j]) \
for i in demand for j in facilities), name="distance")
m.addConstrs((quicksum(assign[i, j] for j in facilities) == 1\
for i in demand), name="assignDemand")
m.addConstrs((assign[i, j] <= isopen[j] for i in demand for j in facilities),\
name="closed")
m.addConstr(n == quicksum(isopen[j] for j in facilities), name="numFacilities")
# zip is needed to iterate over all pairs of consecutive facilites
m.addConstrs((isopen[j] >= isopen[jp1] \
for j, jp1 in zip(facilities, facilities[1:])), name="order")
Note that while it isn't a problem to write floc[j, 0] in the constraint for the distance, you can't write dloc[i, 0] since dloc is a python dictionary and floc is a tupledict.
Setting the objective function und calling m.optimize()
# Objective
m.setObjective(n, sense=GRB.MINIMIZE)
m.optimize()
if m.status == GRB.OPTIMAL:
print(f"Optimal Solution is: {m.objVal}")
print("--------------")
for var in m.getVars():
print(var.varName, var.X)
gives me the optimal solution n = 3.
I am trying to solve the following problem via a Finite Difference Approximation in Python using NumPy:
$u_t = k \, u_{xx}$, on $0 < x < L$ and $t > 0$;
$u(0,t) = u(L,t) = 0$;
$u(x,0) = f(x)$.
I take $u(x,0) = f(x) = x^2$ for my problem.
Programming is not my forte so I need help with the implementation of my code. Here is my code (I'm sorry it is a bit messy, but not too bad I hope):
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
# definition of initial condition function
def f(x):
return x^2
# parameters
L = 1
T = 10
N = 10
M = 100
s = 0.25
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
#x = np.zeros(N+1)
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
#t = np.zeros(M+1)
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# Boundary Conditions
for m in xrange(0, M):
t[m] = m * dt
# Initial Conditions
for j in xrange(0, N):
x[j] = j * dx
# definition of solution to u_t = k * u_xx
u = np.zeros((N+1, M+1)) # NxM array to store values of the solution
# finite difference scheme
for j in xrange(0, N-1):
u[j][0] = x**2 #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1][m] = 0 # Boundary condition
else:
u[j][m+1] = u[j][m] + s * ( u[j+1][m] - #FDM scheme
2 * u[j][m] + u[j-1][m] )
else:
if j == N-1:
u[j+1][m] = 0 # Boundary Condition
print u, t, x
#plt.plot(t, u)
#plt.show()
So the first issue I am having is I am trying to create an array/matrix to store values for the solution. I wanted it to be an NxM matrix, but in my code I made the matrix (N+1)x(M+1) because I kept getting an error that the index was going out of bounds. Anyways how can I make such a matrix using numpy.array so as not to needlessly take up memory by creating a (N+1)x(M+1) matrix filled with zeros?
Second, how can I "access" such an array? The real solution u(x,t) is approximated by u(x[j], t[m]) were j is the jth spatial value, and m is the mth time value. The finite difference scheme is given by:
u(x[j],t[m+1]) = u(x[j],t[m]) + s * ( u(x[j+1],t[m]) - 2 * u(x[j],t[m]) + u(x[j-1],t[m]) )
(See here for the formulation)
I want to be able to implement the Initial Condition u(x[j],t[0]) = x**2 for all values of j = 0,...,N-1. I also need to implement Boundary Conditions u(x[0],t[m]) = 0 = u(x[N],t[m]) for all values of t = 0,...,M. Is the nested loop I created the best way to do this? Originally I tried implementing the I.C. and B.C. under two different for loops which I used to calculate values of the matrices x and t (in my code I still have comments placed where I tried to do this)
I think I am just not using the right notation but I cannot find anywhere in the documentation for NumPy how to "call" such an array so at to iterate through each value in the proposed scheme. Can anyone shed some light on what I am doing wrong?
Any help is very greatly appreciated. This is not homework but rather to understand how to program FDM for Heat Equation because later I will use similar methods to solve the Black-Scholes PDE.
EDIT: So when I run my code on line 60 (the last "else" that I use) I get an error that says invalid syntax, and on line 51 (u[j][0] = x**2 #initial condition) I get an error that reads "setting an array element with a sequence." What does that mean?