I'm writing a not full-implemented Python function using SymPy library which looks for the critical points of a mathematical function f through the KKT conditions, as it follows:
def KKT(f: str, h=[], g=[], max=True):
# NOTE: The expressions contained in g must be such that g <= 0 and the ones contained in h must be such that h = 0. Both g and h are string lists
import sympy as sp # Importing a SymPy library
n = len(h) # Quantity of equality constraints
m = len(g) # Quantity of inequality constraints
f = sp.parse_expr(f) # Constructing the f function by passing a string as an argument
vars = f.free_symbols # Getting the variables set
pars = list(vars) # Parameters list
if n > 0:
for i in range(n):
exec(f'h{i+1} = sp.Symbol("h_{i+1}")')
exec(f'h{i+1} = sp.parse_expr(h[{i}])') # Create the equality constraint h_i
exec(f'l{i+1} = sp.Symbol("\\lambda_{i+1}")') # Create the parameter lambda_i
exec(f'pars.append(l{i+1})') # Adding lambda_i to parameters list
if m > 0:
for j in range(m):
exec(f'g_{j+1} = sp.Symbol("g_{j+1}")')
exec(f'g{j+1} = sp.parse_expr(g[{j}])') # Create the inequality constraint g_i
exec(f'u{j+1} = sp.Symbol("\\mu_{j+1}", negative=False)') # Create the parameter mu_i
exec(f'pars.append(u{j+1})') # Add mu_i to parameters list
exec(f'p{j+1} = sp.Symbol("p_{j+1}", negative=False)') # Create fill portion p_i
exec(f'pars.append(p{j+1})') # Add p_i to parameters list
# Creating the Lagrangean
L = f
if n > 0:
for i in range(n):
exec(f'L = L - l{i+1} * h{i+1}') 'Adding lambda_j * h_j to the Lagrangean
if m > 0:
for j in range(m):
exec(f'L = L - u{j+1} * g{j+1}') # <- THIS LINE IS NOT WORKING
# Adding mu_i * g_i to the Lagrangean
print(f'j: {j}')
print(f'{L}\n')
# Creating the KKT condition from Lagrangean
R = [] # Constraint's set
for var in vars:
R.append(sp.diff(L, var)) # Add the Lagrangean's partial derivative with respect to var
if n > 0:
for i in range(n):
exec(f'R.append(h{i+1})')
if m > 0:
for j in range(m):
exec(f'R.append(u{j+1} * g{j+1})')
exec(f'R.append(g{j+1} + p{j+1})')
# Solving KKT conditions
sols_lagr = sp.solve(R, pars, dict=True) # Lagrangian solutions
critical_points = [{var: sol.get(var) for var in sol if var in vars} for sol in sols_lagr]
return critical_points
KKT('24*x_1 - x_1**2 + 10*x_2 - 2*x_2**2',
[],
['x_1 - 8', 'x_2 - 7', '-x_1', '-x_2']
)
However, for some reason, the following line code doesn't work:
exec(f'L = L - u{j+1} * g{j+1}')
Because I'm getting this result when I execute this code's block:
j: 0
-x_1**2 + 24*x_1 - 2*x_2**2 + 10*x_2
j: 1
-x_1**2 + 24*x_1 - 2*x_2**2 + 10*x_2
j: 2
-x_1**2 + 24*x_1 - 2*x_2**2 + 10*x_2
j: 3
-x_1**2 + 24*x_1 - 2*x_2**2 + 10*x_2
Which shows the Lagrangean is not adding up the parcels. I'd be grateful if someone could help me.
Related
Say, we have f(t) = v * t + A * sin(w * t). I call such functions "saw-like":
I want to solve saw(t) = C, that is, find a root of saw(t) - C (still "saw-like").
I tried writing down a ternary search for function abs(saw(t) - C) to find its minima. If we are lucky (or crafty), it would be the root. Unfortunately, my code does not always work: sometimes we get stuck in those places:
My code (python3):
def calculate(fun):
eps = 0.000000001
eps_l = 0.1
x = terns(fun, 0, 100000000000000)
t = terns(fun, 0, x)
cnt = 0
while fun(x) > eps:
t = x
x = terns(fun, 0, t)
if abs(t - x) < eps_l:
cnt += 1
# A sorry attempt pass some wrong value as a right one.
# Gets us out of an infinite loop at least.
if cnt == 10:
break
return t
def terns(f, l, r):
eps = 0.00000000001
while r - l > eps:
x_1 = l + (r - l) / 3
x_2 = r - (r - l) / 3
if f(x_1) < f(x_2):
r = x_2
else:
l = x_1
return (l + r) / 2
So, how is it done? Is using ternary search the right way?
My other idea was somehow sending the equation over to the net, passing it to Wolfram Alpha and fetching the answers. Yet, I don't how it's done, as I am not quite fluent at python.
How could this be done?
I am trying to solve KKT equations using sympy. All of the equations are symbolic and contain constants that are not given as numbers but as symbols. Alongside with the equations, there are also inequality constraints.
Is it possible to do this in sympy? If not, are there any alternatives?
An example would be:
Doing your question by hand, we get the first Lagrangian to be L = x**2 - bx + 1 - lambda(x - a).
Differentiating with respect to x and lambda and setting to 0, we solve to get x = a, lambda = 2a - b.
So if lambda < 0, we repeat with the new Lagrangian, L = x**2 - bx + 1.
This gives us 2 cases: If 2a - b < 0, then x = b/2, otherwise, x = a.
The following code reproduces this logic. Copy paste this into a file called lagrangian.py and see example 4 for your specific query.
"""
KKT Conditions:
Goal:
To minimise/maximise f(x_) subject to gi(x_) >= 0 for all i and hi(x_) == 0 for all i
where x_ refers to a vector x.
Variables with a `*` after them are optimal quantities.
1. gi(x*) and hi(x*) is feasible (that is, they are satisfied)
2. (df/dxj)(x*) - sum(lambdai* * (dgi/dxj)(x*)) - sum(mui* * (dhi/dxj)(x*)) = 0 for all j
3. lambdai* * gi(x*) = 0 for all i
4. lambdai >= 0 for all i
"""
from sympy import *
from typing import Iterable
def kkt(f, g=None, h=None, x=None):
"""
Finds the optimal values of `x` for `f` given the equalities `g[i] >= 0` for all i
and `h[i] == 0` for all i.
TODO: Remove the private variables _lambda and _mu from the output.
TODO: Make the output more user friendly.
Examples:
>>> from sympy import *
>>> from lagrangian import kkt
>>> x = list(symbols("x:3"))
Example 0 the most basic
>>> kkt(x[0]**2)
([x0], FiniteSet((0,)))
Example 1 from References
>>> kkt(2 * x[0] ** 2 + x[1] ** 2 + 4 * x[2]**2,
... h=[x[0] + 2*x[1] - x[2] - 6, 2 * x[0] - 2 * x[1] + 3 * x[2] - 12])
([_mu0, _mu1, x0, x1, x2], FiniteSet((504/67, 424/67, 338/67, 80/67, 96/67)))
Example 2 from References
>>> kkt(x[0] ** 2 + 2 * x[1] ** 2 + 3 * x[2] ** 2,
... [5 * x[0] - x[1] - 3 * x[2] - 3,
... 2 * x[0] + x[1] + 2 * x[2] - 6])
([_lambda0, x0, x1, x2], FiniteSet((72/35, 72/35, 18/35, 24/35)))
Example 3 from References
>>> kkt(4 * x[0] ** 2 + 2 * x[1] ** 2,
... [-2 * x[0] - 4 * x[1] + 15],
... [3 * x[0] + x[1] - 8])
([_mu0, x0, x1], FiniteSet((64/11, 24/11, 16/11)))
Example 4 for general KKT
>>> t, a, b = symbols("x a b")
>>> kkt(t ** 2 - b * t + 1, t - a, x=t)
Piecewise((([x], FiniteSet((b/2,))), 2*a - b < 0), (([_lambda0, x], FiniteSet((2*a - b, a))), True))
Warnings:
This function uses recursion and if queries are such as example 4 with many inequality
conditions, one will experience heavy performance issues.
References:
.. [1] http://apmonitor.com/me575/index.php/Main/KuhnTucker
Disadvantages:
- Does not allow for arbitrary number of inequalities and equalities.
- Does not work for functions of f that have an infinite
number of turning points and no equality constraints (I think)
"""
# begin sanity checks
if not g:
g = []
if not h:
h = []
if not x:
x = list(f.free_symbols)
if not isinstance(g, Iterable):
g = [g]
if not isinstance(h, Iterable):
h = [h]
if not isinstance(x, Iterable):
x = [x]
# end sanity checks
def grad(func):
"""Returns the grad of an expression or function `func`"""
grad_f = Matrix(len(x), 1, derive_by_array(func, x))
return grad_f
# define our dummy variables for the inequalities
if g:
_lambdas = list(symbols(f"_lambda:{len(g)}", real=True))
sum_g = sum([_lambdas[i] * g[i] for i in range(len(g))])
else:
_lambdas = []
sum_g = 0
# define our dummy variables for the equalities
if h:
_mus = list(symbols(f"_mu:{len(h)}", real=True))
sum_h = sum([_mus[i] * h[i] for i in range(len(h))])
else:
_mus = []
sum_h = 0
# define the lagrangian
lagrangian = f - sum_g - sum_h
# find grad of lagrangian
grad_l = grad(lagrangian)
# 1. feasibility conditions
feasible = Matrix(len(g) + len(h), 1, g + h)
# 2. combine everything into a vector equation
eq = grad_l.row_insert(0, feasible)
solution = solve(eq, x + _lambdas + _mus, set=True)
# 4. remove all non-binding inequality constraints
# for each _lambda solution, add a new solution with that inequality removed
# in the case that it is false.
pieces = []
for i, lamb in enumerate(_lambdas):
new_g = g[:i] + g[i + 1:]
lamb_sol_index = [i for i, s in enumerate(solution[0]) if s == lamb][0]
for solution_piece in solution[1]:
lamb_sol = solution_piece[lamb_sol_index]
try:
if lamb_sol >= 0: # we dont need to check the next kkt if the value is known.
continue
except TypeError: # error when inequality cannot be identified
pass
pieces.append((kkt(f, new_g, h, x), lamb_sol < 0))
pieces.append((solution, True))
return Piecewise(*pieces)
I have a fairly basic model that I am trying to run and I keep getting the following error message:
ERROR: Unexpected exception while running model: The NL writer has detected multiple active objective functions on model unknown, but currently only handles a single objective.
Given that error, I think that CPLEX is interpreting my objective function as a multi-objective problem. What I am trying to do is minimize 'model.obj' which is defined over the indices (i,j,t), but I don't believe this would mean that I have multiple objective functions? The objective function is trying to be formulated as equation 11 below:
from __future__ import division
from pyomo.environ import *
from MPBFunctions import *
# Variable Initialization Matricies
susceptible_init = mpbdata(1,3,1,3)
inf_b4treat_init = mpbdata(1,3,13,15)
##########################################################################################################################
# Set Declaration
##########################################################################################################################
model = ConcreteModel()
Imax = 3
Jmax = 3
Tmax = 2
Kmax = 2
model.Iset = RangeSet(1,Imax) # e.g. i = {1, 2, 3}
model.Jset = RangeSet(1,Jmax)
model.Tset = RangeSet(1,Tmax)
model.Kset = RangeSet(1,Kmax)
##########################################################################################################################
# Parameter Declaration
##########################################################################################################################
##########################################################################################################################
# Variable Declaration
##########################################################################################################################
model.susceptible = Var(model.Iset,model.Jset,model.Tset, initialize=initial_values(3,2,susceptible_init))
model.inf_b4treat = Var(model.Iset,model.Jset,model.Tset, initialize=initial_values(3,2,inf_b4treat_init))
model.inf_treated = Var(model.Iset,model.Jset,model.Tset)
model.level1 = Var(model.Iset,model.Jset,model.Tset,within=Binary)
##########################################################################################################################
# Objective Function
##########################################################################################################################
def objective_rule(model,i,j,t):
return model.obj[i,j,t] == sum(2*model.inf_b4treat[i,j,t] for i in model.Iset for j in model.Jset for t in model.Tset)
model.damages = Objective(model.Iset, model.Jset, model.Tset, rule=objective_rule)
##########################################################################################################################
# Constraint Declaration w/ Imax=Jmax=Tmax = 3 and Kmax = 3
##########################################################################################################################
# Constraint 4: Susceptible recruitment
def susceptible_advance_rule(model, i, j, t):
if t == Tmax:
return Constraint.Skip
else:
return model.susceptible[i, j, t + 1] == model.susceptible[i, j, t] - model.inf_b4treat[i, j, t]
model.susceptible_advance = Constraint(model.Iset, model.Jset, model.Tset, rule=susceptible_advance_rule)
# Constraint 9: Treated Infestation
def treatment_rule(model, i, j, t):
return model.inf_treated[i, j, t] == 0.20 * model.susceptible[i, j,t] * (1 - 0.15 * model.level1[i, j, t])
model.treated_pop = Constraint(model.Iset, model.Jset, model.Tset, rule=treatment_rule)
The objective_rule function should return an expression, you are returning an equality (as if it was a constraint).
def objective_rule(model,i,j,t):
return sum(2*model.inf_b4treat[i,j,t] for i in model.Iset for j in model.Jset for t in model.Tset)
Also double check that your input data are correct.
I want to solve an equation which I am supposed to solve it recursively, I uploaded the picture of formula (Sorry! I did not know how to write mathematical formulas here!)
I wrote the code in Python as below:
import math
alambda = 1.0
rho = 0.8
c = 1.0
b = rho * c / alambda
P0 = (1 - (alambda*b))
P1 = (1-(alambda*b))*(math.exp(alambda*b) - 1)
def a(n):
a_n = math.exp(-alambda*b) * ((alambda*b)**n) / math.factorial(n)
return a_n
def P(n):
P(n) = (P0+P1)*a(n) + sigma(n)
def sigma(n):
j = 2
result = 0
while j <= n+1:
result = result + P(j)*a(n+1-j)
j += 1
return result
It is obvious that I could not finish P function. So please help me with this.
when n=1 I should extract P2, when n=2 I should extract P3.
By the way, P0 and P1 are as written in line 6 and 7.
When I call P(5) I want to see P(0), P(1), P(2), P(3), P(4), P(5), P(6) at the output.
You need to reorganize the formula so that you don't have to calculate P(3) to calculate P(2). This is pretty easy to do, by bringing the last term of the summation, P(n+1)a(0), to the left side of the equation and dividing through by a(0). Then you have a formula for P(n+1) in terms of P(m) where m <= n, which is solvable by recursion.
As Bruce mentions, it's best to cache your intermediate results for P(n) by keeping them in a dict so that a) you don't have to recalculate P(2) etc everytime you need it, and b) after you get the value of P(n), you can just print the dict to see all the values of P(m) where m <= n.
import math
a_lambda = 1.0
rho = 0.8
c = 1.0
b = rho * c / a_lambda
p0 = (1 - (a_lambda*b))
p1 = (1-(a_lambda*b))*(math.exp(a_lambda*b) - 1)
p_dict = {0: p0, 1: p1}
def a(n):
return math.exp(-a_lambda*b) * ((a_lambda*b)**n) / math.factorial(n)
def get_nth_p(n, p_dict):
# return pre-calculated value if p(n) is already known
if n in p_dict:
return p_dict[n]
# Calculate p(n) using modified formula
p_n = ((get_nth_p(n-1, p_dict)
- (get_nth_p(0, p_dict) + get_nth_p(1, p_dict)) * a(n - 1)
- sum(get_nth_p(j, p_dict) * a(n + 1 - j) for j in xrange(2, n)))
/ a(0))
# Save computed value into the dict
p_dict[n] = p_n
return p_n
get_nth_p(6, p_dict)
print p_dict
Edit 2
Some cosmetic updates to the code - shortening the name and making p_dict a mutable default argument (something I try to use only sparingly) really makes the code much more readable:
import math
# Customary to distinguish variables that are unchanging by making them ALLCAP
A_LAMBDA = 1.0
RHO = 0.8
C = 1.0
B = RHO * C / A_LAMBDA
P0 = (1 - (A_LAMBDA*B))
P1 = (1-(A_LAMBDA*B))*(math.exp(A_LAMBDA*B) - 1)
p_value_cache = {0: P0, 1: P1}
def a(n):
return math.exp(-A_LAMBDA*B) * ((A_LAMBDA*B)**n) / math.factorial(n)
def p(n, p_dict=p_value_cache):
# return pre-calculated value if p(n) is already known
if n in p_dict:
return p_dict[n]
# Calculate p(n) using modified formula
p_n = ((p(n-1)
- (p(0) + p(1)) * a(n - 1)
- sum(p(j) * a(n + 1 - j) for j in xrange(2, n)))
/ a(0))
# Save computed value into the dict
p_dict[n] = p_n
return p_n
p(6)
print p_value_cache
You could fix if that way:
import math
alambda = 1.0
rho = 0.8
c = 1.0
b = rho * c / alambda
def a(n):
# you might want to cache a as well
a_n = math.exp(-alambda*b) * ((alambda*b)**n) / math.factorial(n)
return a_n
PCache={0:(1 - (alambda*b)),1:(1-(alambda*b))*(math.exp(alambda*b) - 1)}
def P(n):
if n in PCache:
return PCache[n]
ret= (P(0)+P(1))*a(n) + sigma(n)
PCache[n]=ret
return ret
def sigma(n):
# caching this seems smart as well
j = 2
result = 0
while j <= n+1:
result = result + P(j)*a(n+1-j)
j += 1
return result
void displayP(n):
P(n) # fill cache :-)
for x in range(n):
print ("%u -> %d\n" % (x,PCache[x]))
Instead of managing the cache manually, you might want to use a memoize decorator (see http://www.python-course.eu/python3_memoization.php )
Notes:
not tested, but you should get the idea behind it
your recurrence won't work P(n) depends on P(n+1) on your equation... This will never end
It looks like I misunderstood P0 and P1 as being Both constants (big numbers) and results (small numbers, indices)... Notation is not the best choice I guess...
I am trying to write a program that calculates the optimum amount to bet based on log utility and simultaneous dependent events.
In order to do this I am trying to use the numpy.optimize.fmin function. The function anon that I am passing to it works and produces (hopefully) correct output but when numpy tries to optimise the function I get the following error
s[i].append(f[i][0]*w[i][0] + f[i][1]*w[i][1])
IndexError: invalid index to scalar variable.
Since I have no idea about fmin, I have no idea what is causing this error.
My code is below, hopefully not tl;dr but I wouldn't blame you.
APPENDIX
def main():
p = [[0.1,0.1,0.2, 0.2,0.1,0, 0.1,0.1,0.1]]
w = [[5,4]]
MaxLU(p,w,True)
def MaxLU(p, w, Push = False, maxIter = 10):
#Maximises LU, using Scipy in built function
if Push == True:
anon = lambda f: -PushLogUtility(p, w, f)
else:
anon = lambda f: -LogUtility(p, w, f)
#We use multiple random starts
f = []
LU = []
for i in range(0,maxIter):
start = np.random.rand(len(p))
start = start / 5 * np.sum(start)
f.append(optimize.fmin(anon, start)) #Error occurs in here!
if Push == True:
LU.append(PushLogUtility(p, w, f[-1]))
else:
LU.append(LogUtility(p, w, f[-1]))
#Now find the index of the max LU and return that same index of f
return f[LU.index(np.max(LU))]
def PushLogUtility(p,w,f):
#Outputs log utility incoroporating pushes and dependent totals, money data
#p : 9xk length vector of joint probabilities for each of the k games, p = [[p_(W_T W_M), p_(W_T P_M), p_(W_T L_M), p_(P_T W_M) ... ]]
#w : 2xk matrix of odds where w = [[total odds, money odds] ... ]
#f : 2xk matrix of bankroll percentages to bet, f = [[f_T, f_M] ... ]
utility = 0
k = len(p)
s = k*[[]]
for i in range(0,k):
s[i].append(f[i][0]*w[i][0] + f[i][1]*w[i][1])
s[i].append(f[i][0]*w[i][0])
s[i].append(f[i][0]*w[i][0] - f[i][1])
s[i].append(f[i][1]*w[i][1])
s[i].append(0)
s[i].append(-f[i][1])
s[i].append(-f[i][0] - f[i][1])
s[i].append(-f[i][0] - f[i][1])
s[i].append(-f[i][0] - f[i][1])
for i in range(0,9 ** k):
l = de2ni(i) #Converts number to base 9
if i == 0:
l += int(math.ceil(k - 1 - math.log(i + 1,9))) * [0]
else:
l += int(math.ceil(k - 1 - math.log(i,9))) * [0]
productTerm = np.prod([p[i][l[i]] for i in range(0,k)])
sumTerm = np.sum([s[i][l[i]] for i in range(0,k)])
utility = utility + productTerm * np.log(1 + sumTerm)
return utility
Here where you do:
s[i].append(f[i][0]*w[i][0] + f[i][1]*w[i][1])
if you look at the types, you'll find s[i] is a [], f[i] is 0.104528 and w[i] is [5,4]. You then try to index f[i] a second time - which is not possible and causes the error.