-
Notifications
You must be signed in to change notification settings - Fork 2
/
numdiff_utils.py
61 lines (51 loc) · 1.89 KB
/
numdiff_utils.py
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
import numpy as np
def numdiff(myfunc, x, par, nargout=1):
'''
computes gradient and hessian of myfunc numerically
:param myfunc: pointer to either of f1 or f2
:param x: Input vector R^(mx1)
:param par: a dictionary including keys:
'epsilon' : The incerement of x
'f_par' : parameters dictionary given to function
'gradient' : gradient function of f
:param nargout: Like nargout of matlab, can be 1 or 2
:return: [gnum, Hnum]
gnum : Numerical estimation of function gradient
Hnum : Numerical estimation of function Hessian
'''
assert callable(myfunc)
assert isinstance(x, np.ndarray)
assert isinstance(par, dict)
assert 'epsilon' in par.keys()
assert isinstance(nargout, int)
assert nargout in range(1, 3)
epsilon_tot = par['epsilon']
assert isinstance(epsilon_tot, float)
max_abs_val_of_x = max(x.min(), x.max(), key=abs)
if max_abs_val_of_x != 0:
epsilon = pow(epsilon_tot, 1 / 3) * max_abs_val_of_x
else:
epsilon = epsilon_tot**2
# standard_base = np.array(((1, 0, 0),
# (0, 1, 0),
# (0, 0, 1)))
standard_base = np.identity(len(x))
grad = []
for i in range(0, len(x)):
right_g_i = myfunc(x+epsilon*standard_base[i])
left_g_i = myfunc(x-epsilon*standard_base[i])
g_i = (right_g_i - left_g_i)/(2*epsilon)
grad.append(g_i)
grad = np.array(grad)
if nargout == 1:
return grad
hess = []
analytic_grad = par['gradient']
assert callable(analytic_grad)
for i in range(0, len(x)):
right_sample = analytic_grad(x+epsilon*standard_base[i], par['f_par'])
left_sample = analytic_grad(x-epsilon*standard_base[i], par['f_par'])
h_i = (right_sample-left_sample)/(2*epsilon)
hess.append(h_i)
hess = np.array(hess)
return grad, hess