Skip to content

Commit 152c067

Browse files
committed
adding jitted scalar maximization routine, first build
1 parent 649cc55 commit 152c067

File tree

5 files changed

+182
-1
lines changed

5 files changed

+182
-1
lines changed

quantecon/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from . import game_theory
1313
from . import quad
1414
from . import random
15+
from . import optimize
1516

1617
#-Objects-#
1718
from .compute_fp import compute_fixed_point

quantecon/optimize/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""
2+
Initialization of the optimize subpackage
3+
"""
4+
5+
from .scalar_maximization import maximize_scalar
6+
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import numpy as np
2+
from numba import jit, njit
3+
4+
@njit
5+
def maximize_scalar(func, a, b, xtol=1e-5, maxiter=500):
6+
"""
7+
Uses a jitted version of the maximization routine from SciPy's fminbound.
8+
The algorithm is identical except that it's been switched to maximization
9+
rather than minimization, and the tests for convergence have been stripped
10+
out to allow for jit compilation.
11+
12+
Note that the input function `func` must be jitted or the call will fail.
13+
14+
Parameters
15+
----------
16+
maxiter : int, optional
17+
Maximum number of iterations to perform.
18+
xtol : float, optional
19+
Absolute error in solution `xopt` acceptable for convergence.
20+
func : jitted function
21+
a : scalar
22+
Lower bound for search
23+
b : scalar
24+
Upper bound for search
25+
26+
Returns
27+
-------
28+
fval : float
29+
The maximum value attained
30+
xf : float
31+
The maxizer
32+
33+
Example
34+
-------
35+
36+
```
37+
@njit
38+
def f(x):
39+
return -(x + 2.0)**2 + 1.0
40+
41+
fval, xf = maximize_scalar(f, -2, 2)
42+
```
43+
44+
"""
45+
maxfun = maxiter
46+
47+
sqrt_eps = np.sqrt(2.2e-16)
48+
golden_mean = 0.5 * (3.0 - np.sqrt(5.0))
49+
50+
fulc = a + golden_mean * (b - a)
51+
nfc, xf = fulc, fulc
52+
rat = e = 0.0
53+
x = xf
54+
fx = -func(x)
55+
num = 1
56+
fmin_data = (1, xf, fx)
57+
58+
ffulc = fnfc = fx
59+
xm = 0.5 * (a + b)
60+
tol1 = sqrt_eps * np.abs(xf) + xtol / 3.0
61+
tol2 = 2.0 * tol1
62+
63+
while (np.abs(xf - xm) > (tol2 - 0.5 * (b - a))):
64+
golden = 1
65+
# Check for parabolic fit
66+
if np.abs(e) > tol1:
67+
golden = 0
68+
r = (xf - nfc) * (fx - ffulc)
69+
q = (xf - fulc) * (fx - fnfc)
70+
p = (xf - fulc) * q - (xf - nfc) * r
71+
q = 2.0 * (q - r)
72+
if q > 0.0:
73+
p = -p
74+
q = np.abs(q)
75+
r = e
76+
e = rat
77+
78+
# Check for acceptability of parabola
79+
if ((np.abs(p) < np.abs(0.5*q*r)) and (p > q*(a - xf)) and
80+
(p < q * (b - xf))):
81+
rat = (p + 0.0) / q
82+
x = xf + rat
83+
84+
if ((x - a) < tol2) or ((b - x) < tol2):
85+
si = np.sign(xm - xf) + ((xm - xf) == 0)
86+
rat = tol1 * si
87+
else: # do a golden section step
88+
golden = 1
89+
90+
if golden: # Do a golden-section step
91+
if xf >= xm:
92+
e = a - xf
93+
else:
94+
e = b - xf
95+
rat = golden_mean*e
96+
97+
if rat == 0:
98+
si = np.sign(rat) + 1
99+
else:
100+
si = np.sign(rat)
101+
102+
x = xf + si * np.maximum(np.abs(rat), tol1)
103+
fu = -func(x)
104+
num += 1
105+
fmin_data = (num, x, fu)
106+
107+
if fu <= fx:
108+
if x >= xf:
109+
a = xf
110+
else:
111+
b = xf
112+
fulc, ffulc = nfc, fnfc
113+
nfc, fnfc = xf, fx
114+
xf, fx = x, fu
115+
else:
116+
if x < xf:
117+
a = x
118+
else:
119+
b = x
120+
if (fu <= fnfc) or (nfc == xf):
121+
fulc, ffulc = nfc, fnfc
122+
nfc, fnfc = x, fu
123+
elif (fu <= ffulc) or (fulc == xf) or (fulc == nfc):
124+
fulc, ffulc = x, fu
125+
126+
xm = 0.5 * (a + b)
127+
tol1 = sqrt_eps * np.abs(xf) + xtol / 3.0
128+
tol2 = 2.0 * tol1
129+
130+
if num >= maxfun:
131+
break
132+
133+
fval = -fx
134+
135+
return fval, xf
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Tests for scalar maximization.
3+
4+
"""
5+
import numpy as np
6+
from numpy.testing import assert_almost_equal
7+
from numba import njit
8+
9+
from quantecon.optimize import maximize_scalar
10+
11+
@njit
12+
def f(x):
13+
"""
14+
A function for testing on.
15+
"""
16+
return -(x + 2.0)**2 + 1.0
17+
18+
def test_maximize_scalar():
19+
"""
20+
Uses the function f defined above to test the scalar maximization
21+
routine.
22+
"""
23+
true_fval = 1.0
24+
true_xf = -2.0
25+
fval, xf = maximize_scalar(f, -2, 2)
26+
assert_almost_equal(true_fval, fval, decimal=4)
27+
assert_almost_equal(true_xf, xf, decimal=4)
28+
29+
30+
if __name__ == '__main__':
31+
import sys
32+
import nose
33+
34+
argv = sys.argv[:]
35+
argv.append('--verbose')
36+
argv.append('--nocapture')
37+
nose.main(argv=argv, defaultTest=__file__)
38+
39+

quantecon/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""
22
This is a VERSION file and should NOT be manually altered
33
"""
4-
version = '0.3.7'
4+
version = '0.3.8'

0 commit comments

Comments
 (0)