Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/powermeanderiv #83

Merged
merged 2 commits into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 32 additions & 14 deletions mogp_emulator/MeanFunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,9 +995,14 @@ def mean_deriv(self, x, params):
deriv[:switch] = (exp*self.f1.mean_f(x, params[:switch])**(exp - 1.)*
self.f1.mean_deriv(x, params[:switch]))

deriv[switch:] = (np.log(self.f1.mean_f(x, params[:switch]))*
self.f1.mean_f(x, params[:switch])**exp*
self.f2.mean_deriv(x, params[switch:]))
# only evaluate if f2 has parameters, as f1 could be negative and taking the log will
# raise an error even though this calculation is ultimately ignored in this case

if not self.f2.get_n_params(x) == 0:

deriv[switch:] = (np.log(self.f1.mean_f(x, params[:switch]))*
self.f1.mean_f(x, params[:switch])**exp*
self.f2.mean_deriv(x, params[switch:]))

return deriv

Expand Down Expand Up @@ -1038,20 +1043,33 @@ def mean_hessian(self, x, params):

hess = np.zeros((self.get_n_params(x), self.get_n_params(x), x.shape[0]))

if nonzeroexp or nononeexp:
if nonzeroexp and nononeexp:

hess[:switch, :switch] = (exp*self.f1.mean_f(x, params[:switch])**(exp - 1.)*
self.f1.mean_hessian(x, params[:switch]) +
exp*(exp - 1.)*self.f1.mean_f(x, params[:switch])**(exp - 2.)*
self.f1.mean_deriv(x, params[:switch]))

elif nonzeroexp:

hess[:switch, :switch] = (exp*(exp - 1)*self.f1.mean_f(x, params[:switch])**(exp - 2.)*
hess[:switch, :switch] = (exp*self.f1.mean_f(x, params[:switch])**(exp - 1.)*
self.f1.mean_hessian(x, params[:switch]))

if nonzeroexp:
hess[:switch, switch:, :] = (exp*(exp - 1)*self.f1.mean_f(x, params[:switch])**(exp - 2.)*
self.f1.mean_deriv(x, params[:switch])[:,np.newaxis,:]*
self.f2.mean_deriv(x, params[switch:])[np.newaxis,:,:])
hess[switch:, :switch, :] = np.transpose(hess[:switch, switch:, :], (1, 0, 2))

if nonzeroexp or nononeexp:
hess[switch:, switch:] = (exp*(exp - 1)*self.f1.mean_f(x, params[:switch])**(exp - 2.)*
self.f2.mean_hessian(x, params[switch:]))

if not self.f2.get_n_params(x) == 0:

if nonzeroexp:
hess[:switch, switch:, :] = (self.f1.mean_f(x, params[:switch])**(exp - 1.)*
(exp*np.log(self.f1.mean_f(x, params[:switch])) + 1.)*
self.f1.mean_deriv(x, params[:switch])[:,np.newaxis,:]*
self.f2.mean_deriv(x, params[switch:])[np.newaxis,:,:])
hess[switch:, :switch, :] = np.transpose(hess[:switch, switch:, :], (1, 0, 2))

hess[switch:, switch:] = (self.f1.mean_f(x, params[:switch])**exp*
(np.log(self.f1.mean_f(x, params[:switch]))**2*
self.f2.mean_deriv(x, params[switch:])**2 +
np.log(self.f1.mean_f(x, params[:switch]))*
self.f2.mean_hessian(x, params[switch:])))

return hess

Expand Down
57 changes: 54 additions & 3 deletions mogp_emulator/tests/test_MeanFunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,21 +484,31 @@ def test_MeanPower(x, zeroparams, oneparams, dx):

assert mf4.get_n_params(x) == n_params

deriv_exp_0 = np.zeros((n_params, x.shape[0]))
deriv_exp_0[0] = np.log(x[:,0])*x[:,0]**0
deriv_exp_1 = np.zeros((n_params, x.shape[0]))
deriv_exp_1[0] = np.log(x[:,0])*x[:,0]**oneparams[0]
deriv_exp_2 = np.zeros((n_params, x.shape[0]))
deriv_exp_2[0] = np.log(x[:,0])*x[:,0]**params[0]
hess_exp_0 = np.zeros((n_params, n_params, x.shape[0]))
hess_exp_0[0, 0] = (np.log(x[:,0])**2)*x[:,0]**0
hess_exp_1 = np.zeros((n_params, n_params, x.shape[0]))
hess_exp_1[0, 0] = (np.log(x[:,0])**2)*x[:,0]**oneparams[0]
hess_exp_2 = np.zeros((n_params, n_params, x.shape[0]))
hess_exp_2[0, 0] = (np.log(x[:,0])**2)*x[:,0]**params[0]
inputderiv_exp_1 = np.zeros((x.shape[1], x.shape[0]))
inputderiv_exp_1[0] = oneparams[0]*x[:,0]**(oneparams[0] - 1.)
inputderiv_exp_2 = np.zeros((x.shape[1], x.shape[0]))
inputderiv_exp_2[0] = params[0]*x[:,0]**(params[0] - 1.)

assert_allclose(mf4.mean_f(x, oneparams), x[:,0]**oneparams[0])
assert_allclose(mf4.mean_f(x, params), x[:,0]**params[0])
assert_allclose(mf4.mean_deriv(x, np.zeros(1)), deriv_exp_0)
assert_allclose(mf4.mean_deriv(x, oneparams), deriv_exp_1)
assert_allclose(mf4.mean_deriv(x, params), deriv_exp_2)
assert_allclose(mf4.mean_hessian(x, oneparams), np.zeros((n_params, n_params, x.shape[0])))
assert_allclose(mf4.mean_hessian(x, params), np.zeros((n_params, n_params, x.shape[0])))
assert_allclose(mf4.mean_hessian(x, np.zeros(1)), hess_exp_0)
assert_allclose(mf4.mean_hessian(x, oneparams), hess_exp_1)
assert_allclose(mf4.mean_hessian(x, params), hess_exp_2)
assert_allclose(mf4.mean_inputderiv(x, oneparams), inputderiv_exp_1)
assert_allclose(mf4.mean_inputderiv(x, params), inputderiv_exp_2)

Expand All @@ -518,6 +528,24 @@ def test_MeanPower(x, zeroparams, oneparams, dx):

assert_allclose(mf4.mean_deriv(x, params), deriv_fd, atol=1.e-5, rtol=1.e-5)

hess_fd = np.zeros((n_params, n_params, x.shape[0]))
for i in range(n_params):
for j in range(n_params):
dx_array = np.zeros(n_params)
dx_array[i] = dx
hess_fd[i, j] = (mf4.mean_deriv(x, oneparams)[j] - mf4.mean_deriv(x, oneparams - dx_array)[j])/dx

assert_allclose(mf4.mean_hessian(x, oneparams), hess_fd, atol=1.e-5, rtol=1.e-5)

hess_fd = np.zeros((n_params, n_params, x.shape[0]))
for i in range(n_params):
for j in range(n_params):
dx_array = np.zeros(n_params)
dx_array[i] = dx
hess_fd[i, j] = (mf4.mean_deriv(x, params)[j] - mf4.mean_deriv(x, params - dx_array)[j])/dx

assert_allclose(mf4.mean_hessian(x, params), hess_fd, atol=1.e-5, rtol=1.e-5)

inputderiv_fd = np.zeros((D, x.shape[0]))
for i in range(D):
dx_array = np.zeros(D)
Expand All @@ -540,7 +568,7 @@ def test_MeanPower(x, zeroparams, oneparams, dx):

assert_allclose(mf5.mean_f(x, params), params[0]**2)
assert_allclose(mf5.mean_deriv(x, params), np.broadcast_to(2.*params[0], (n_params, x.shape[0])))
assert_allclose(mf5.mean_hessian(x, params), np.zeros((n_params, n_params, x.shape[0])))
assert_allclose(mf5.mean_hessian(x, params), np.broadcast_to(2., (n_params, n_params, x.shape[0])))
assert_allclose(mf5.mean_inputderiv(x, params), np.zeros((x.shape[1], x.shape[0])))

deriv_fd = np.zeros((n_params, x.shape[0]))
Expand All @@ -551,6 +579,29 @@ def test_MeanPower(x, zeroparams, oneparams, dx):

assert_allclose(mf5.mean_deriv(x, params), deriv_fd, atol=1.e-5, rtol=1.e-5)

def test_MeanPower_specialcases():
"test situations where MeanPower is badly behaved (complex, etc.) or could behave badly if not well implemented"

# mean function should raise an error if not real

mf = LinearMean(0)**2.1

with pytest.raises(FloatingPointError):
mf.mean_f(np.array([-2.]), np.zeros(0))

# verify that if exponent has no parameters and x is negative still get correct functioning

mf = LinearMean(0)**2.

assert_allclose(mf.mean_deriv(np.array([-2.]), np.zeros(0)), -4.)

# check that error raised for badly behaved derivative

mf = LinearMean(0)**Coefficient()

with pytest.raises(FloatingPointError):
mf.mean_deriv(np.array([-2.]), np.array([2.]))

def test_MeanComposite(x, params, dx):
"test the composite mean function"

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
MAJOR = 0
MINOR = 3
MICRO = 0
PRERELEASE = 3
PRERELEASE = 4
ISRELEASED = False
version = "{}.{}.{}".format(MAJOR, MINOR, MICRO)

Expand Down