To calculate electrical circuits, I encountered the problem of an incorrect response from the minimize functions when calculating circuits with a parallel connection. For example this:
Creating a matrix of connections and fill in the resistance matrix: Equation of the form: A*x = V ,where function sum(F * x**3) -> minimum
import numpy as np
from scipy.optimize import minimize, LinearConstraint
def CreateArrays(A,V):
A = np.array(A) #matrix of connections
V = np.array(V)
buf = [0.1 for i in range(len(A[0]))]
x0 = np.array(buf)
return A,V,x0
def start(A,V,F):
def obj_func(x):
return np.sum(F * x**3)
A,V,x0 = CreateArrays(A,V)
lincon = LinearConstraint(A, lb=V, ub=V)
res = minimize(obj_func, x0, method='trust-constr', constraints=lincon)
return res.x
#Matrix of connections, vector V, vector F
I = start([[1, 0, 1], [0, -1, -1], [-1, 1, 0]],
[100, -100, 0],[1e-4, 1e-4, 8e-4])
print(I)
The result is [33.36666667 33.36666667 66.63333333] but it's a mistake; the true result is [66.63333333, 66.63333333, 33.36666667], where value of function is minimum. The higher the resistance, the lower the electric currents.
I find minimum with this code:
f = [1e-4,1e-4,8e-4]
s=0
save = 0.5
x = [0.5,0.5,99.5]
for i in range(len(x)):
s = s + f[i] * x[i]**3
mini = s #start minimum
print(mini)
j = 0.5
while j < 100:
x = [j, j, 100-j]
s=0
for i in range(len(x)):
s = s + f[i] * x[i]**3
if s < mini:
mini = s
save = x
j =j + 0.5
print(mini,save)
It seems like the problem is here:
lincon = LinearConstraint(A, lb=V, ub=V)
This constraint seemed to mess up SLSQP, COBYLA, and trust-constr. SLSQP complains about singular constraint matrices. COBYLA complains that it only supports ineq constraints, not eq constraints. trust-constr works, but make no progress once it has found a solution that satisfies the constraints.
I found two things that helped.
The first was to use a better initial guess for x0, by starting with something that obeys the constraints.
def CreateArrays(A,V):
A = np.array(A) #matrix of connections
V = np.array(V)
x0 = np.linalg.lstsq(A, V, rcond=None)[0]
return A,V,x0
The second thing was to give the linear constraint some wiggle room, and allow it to be off in either direction by an epsilon. Initially, the linear constraint is allowed to vary by 0.001, and it is reduced from there.
def start(A,V,F):
def obj_func(x):
ret = np.sum(F * x**3)
return ret
A,V,x0 = CreateArrays(A,V)
for eps in [1e-3, 1e-6, 1e-9, 0]:
lincon = LinearConstraint(A, lb=V - eps, ub=V + eps)
res = minimize(obj_func, x0, method='trust-constr', constraints=lincon)
x0 = res.x
return res.x
With these changes, trust-constr and COBYLA can both solve this.