-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathold_solutions_analytic.py
More file actions
173 lines (126 loc) · 5.07 KB
/
old_solutions_analytic.py
File metadata and controls
173 lines (126 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# attempts at a solver for the general system
import numpy as np
import pdb
from scipy.integrate import solve_bvp
from scipy.integrate import quad
from warnings import warn
def defaultInit(p, M, binding, domTheta):
""" initialization:
somewhat nice versions that I made by hand
"""
ss = np.linspace(p.a, p.b, 300)
thetaGuess = (domTheta[1] - domTheta[0]) * p.cdf(ss) + domTheta[0]
qGuess = 5*(thetaGuess - 1.02*domTheta[1])
yGuess = 5.*M*ss.copy() # np.zeros(ss.shape) + 0.01
stateGuess = np.array([thetaGuess, qGuess, yGuess])
lmbdCGuess = [-1.]
return ss, stateGuess, lmbdCGuess
def findSol(p, LprimeInv, Cfuns, M, binding=True, domTheta=(0, 1),
getInitial=defaultInit, lenBinding=True, **solverKwargs):
""" runs the boundary value problem solver
M is the value of the constraint, domTheta are theta bounds
"""
bndConds = getBoundary(binding, lenBinding, M, domTheta)
# differential equation
toSolve = lambda t, state, lmbdC: PMPsystem(t, state, lmbdC, p.pdf, LprimeInv, Cfuns)
# initial conditions
ss, stateGuess, lmbdCGuess = getInitial(p, M, binding, domTheta)
# solve it
sol = solve_bvp(toSolve, bndConds, ss, stateGuess, p=lmbdCGuess, **solverKwargs)
return sol
def PMPsystem(s, state, lmbdC, pdf, LprimeInv, Cfuns):
""" System to solve from optimal control.
lmbdC is the corresponding lagrange multiplier
"""
theta, q, y = state
C, dC = Cfuns
lmbdC = lmbdC[0]
u = LprimeInv(-q / pdf(s))
dtheta = u
dq = -lmbdC * dC(theta) * pdf(s)
dy = C(theta) * pdf(s)
return np.array([dtheta, dq, dy])
def getBoundary(constrBinding, lenBinding, M, domTheta=(0, 1)):
""" boundary conditions with the correct slacknesses
"""
def bndConds(stateI, stateF, lmbdC):
""" Boundary conditions """
thetaI = stateI[0]
thetaF = stateF[0]
qF = stateF[1]
yI = stateI[2] # additional boundary condition: yI starts at 0!
yF = stateF[2]
if lenBinding:
if constrBinding:
# both binding
return np.array([thetaI - domTheta[0], thetaF - domTheta[1],
yI - 0., yF - M])
else:
# only the length is binding
return np.array([thetaI - domTheta[0], thetaF - domTheta[1],
yI - 0., lmbdC[0] - 0.])
else:
# I'm treating the LHS as binding, but this doesn't have to be the case either
if constrBinding:
# only the constraint is binding
return np.array([thetaI - domTheta[0], qF - 0., yI - 0., yF - M])
else:
# neither binding
return np.array([thetaI - domTheta[0], qF - 0., yI - 0., lmbdC[0] - 0.])
return bndConds
def ratchetConstraint(p, LprimeInv, Cfuns, M, binding=True, domTheta=(0, 1),
maxSteps=30, **solverKwargs):
"""
Solver that handles non-binding constraints.
Ratchets down the constraint until it is achieved.
"""
unConstrained = findSol(p, LprimeInv, Cfuns, M, binding=False, **solverKwargs)
# could we actually solve the unconstrained version?
if not unConstrained.success:
raise Exception('Failure on unconstrained. Bad default init')
if not binding:
# unconstrained is what we're looking for
return unConstrained
# we are actually constrained
C = Cfuns[0]
toInt = lambda s: C(unConstrained.sol(s)[0]) * p.pdf(s)
M_0 = quad(toInt, p.a, p.b)[0]
# test if the constraint is slack or tight
if M > M_0:
warn('Slack Constraint')
print('slack', M_0)
return unConstrained
memo = {M_0: unConstrained}
def ratchetDown(M, Mclosest, depth):
""" Approaches the constrained M from above """
print(M, Mclosest)
if M in memo:
return memo[M]
if depth > maxSteps:
raise Exception('Failure to converge to the solution!')
# initialize to the closest M seen
testSol = findSol(p, LprimeInv, Cfuns, M, binding=True,
getInitial=makeInitializer(memo[Mclosest], Mclosest),
**solverKwargs)
if testSol.success:
# made it!
return testSol
# Else: find the half way point and try again with that point
Mtest = (M + Mclosest) / 2
MtestSol = ratchetDown(Mtest, Mclosest, depth+1)
memo[Mtest] = MtestSol
return ratchetDown(M, Mtest, depth+1)
constrainedSol = ratchetDown(M, M_0, 0)
return constrainedSol
def makeInitializer(previous, Mprev):
""" for ratcheting down the value of the constraint:
initialize to previously solved cases """
def fancyInit(p, M, *args):
ss = np.linspace(p.a, p.b, 300)
stateGuess = previous.sol(ss)
stateGuess[0] *= M / Mprev
stateGuess[1] *= Mprev / M
stateGuess[2] *= M / Mprev
lmbdCGuess = previous.p
return ss, stateGuess, lmbdCGuess
return fancyInit