def test_christoffel_inv_gradients(self):
        degree = 2
        ab = jacobi_recurrence(degree + 1, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree,
                            ab=ab)
        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree,
                                    ab=ab,
                                    deriv_order=1)
        sample = np.random.uniform(-1, 1, (1, 1))
        #sample = np.atleast_2d(-0.99)

        fun = partial(christoffel_function_inv_1d, basis_fun)
        jac = partial(christoffel_function_inv_jac_1d, basis_fun_and_jac)

        #xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        #plt.plot(sample[0], fun(sample), 'o'); plt.show()

        err = check_gradients(fun, jac, sample)
        assert err.max() > .5 and err.min() < 1e-7

        basis_fun_jac_hess = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                     nmax=degree,
                                     ab=ab,
                                     deriv_order=2)
        hess = partial(christoffel_function_inv_hess_1d,
                       basis_fun_jac_hess,
                       normalize=False)
        err = check_gradients(jac, hess, sample)
        assert err.max() > .5 and err.min() < 1e-7
Example #2
0
    def test_smooth_conditioal_value_at_risk_gradient(self):
        smoother_type, eps, alpha = 0, 1e-1, 0.7
        samples = np.linspace(-1, 1, 11)
        t = 0.1
        x0 = np.concatenate([samples, [t]])[:, np.newaxis]
        errors = check_gradients(
            partial(smooth_conditional_value_at_risk, smoother_type, eps,
                    alpha),
            partial(smooth_conditional_value_at_risk_gradient, smoother_type,
                    eps, alpha), x0)
        assert errors.min() < 1e-6

        weights = np.random.uniform(1, 2, samples.shape[0])
        weights /= weights.sum()
        errors = check_gradients(
            partial(smooth_conditional_value_at_risk,
                    smoother_type,
                    eps,
                    alpha,
                    weights=weights),
            partial(smooth_conditional_value_at_risk_gradient,
                    smoother_type,
                    eps,
                    alpha,
                    weights=weights), x0)
        assert errors.min() < 1e-6
Example #3
0
    def help_check_smooth_max_function_gradients(self, smoother_type, eps):
        x = np.array([0.01])
        errors = check_gradients(
            partial(smooth_max_function, smoother_type, eps),
            partial(smooth_max_function_first_derivative, smoother_type, eps),
            x[:, np.newaxis])

        errors = check_gradients(
            partial(smooth_max_function_first_derivative, smoother_type, eps),
            partial(smooth_max_function_second_derivative, smoother_type, eps),
            x[:, np.newaxis])
        assert errors.min() < 1e-6
Example #4
0
    def test_objective_derivatives(self):
        smoother_type, eps = 'quintic', 5e-1
        nsamples, degree = 10, 1

        samples, values, fun, jac, probabilities, ncoef, x0 = \
            self.setup_linear_regression_problem(nsamples, degree, eps)

        x0 -= eps / 3

        eta = np.arange(nsamples // 2, nsamples)
        problem = FSDOptProblem(values, fun, jac, None, eta, probabilities,
                                smoother_type, eps, ncoef)

        # assert smooth function is shifted correctly
        assert problem.smooth_fun(np.array([[0.]])) == 1.0

        err = check_gradients(problem.objective_fun,
                              problem.objective_jac,
                              x0,
                              rel=False)
        # fd difference error should exhibit V-cycle. These values
        # test this for this specific problem
        assert err.min() < 1e-6 and err.max() > 0.1
        err = check_hessian(problem.objective_jac,
                            problem.objective_hessp,
                            x0,
                            rel=False)
        # fd hessian  error should decay linearly (assuming first order fd)
        # because hessian is constant (indendent of x)
        assert err[0] < 1e-14 and err[10] > 1e-9
        # fd difference error should exhibit V-cycle. These values
        # test this for this specific problem
        err = check_gradients(problem.constraint_fun,
                              problem.constraint_jac,
                              x0,
                              rel=False)
        assert err.min() < 1e-7 and err.max() > 0.1

        lmult = np.random.normal(0, 1, (eta.shape[0]))

        def constr_jac(x):
            jl = problem.constraint_jac(x).T.dot(lmult)
            return jl

        def constr_hessp(x, v):
            hl = problem.constraint_hess(x, lmult).dot(v)
            return hl

        err = check_hessian(constr_jac, constr_hessp, x0)
        assert err.min() < 1e-5 and err.max() > 0.1
Example #5
0
    def help_check_smooth_conditional_value_at_risk_composition_gradient(
            self, smoother_type, eps, alpha, nsamples, nvars):
        samples = np.arange(nsamples*nvars).reshape(nvars, nsamples)
        t = 0.1
        x0 = np.array([2, 3, t])[:, np.newaxis]
        def fun(x): return (np.sum((x*samples)**2, axis=0).T)[:, np.newaxis]
        def jac(x): return 2*(x*samples**2).T

        errors = check_gradients(fun, jac, x0[:2], disp=False)
        assert (errors.min() < 1e-6)

        errors = check_gradients(
            partial(smooth_conditional_value_at_risk_composition,
                    smoother_type, eps, alpha, fun, jac),
            True, x0)
        assert errors.min() < 1e-7
Example #6
0
    def test_least_squares(self):
        """for tutorial purposes. Perhaps move to a tutorial"""
        np.random.seed(1)
        tol = 1e-14
        nsamples, degree, sparsity = 20, 7, 2
        samples = np.random.uniform(0, 1, (1, nsamples))
        basis_matrix = samples.T**np.arange(degree + 1)[np.newaxis, :]

        true_coef = np.zeros((basis_matrix.shape[1], 1))
        true_coef[np.random.permutation(true_coef.shape[0])[:sparsity]] = 1.
        vals = basis_matrix.dot(true_coef)

        def objective(x, return_jac=True):
            residual = basis_matrix.dot(x) - vals
            obj = 0.5 * residual.T.dot(residual)[0, 0]
            jac = basis_matrix.T.dot(residual).T
            if return_jac:
                return obj, jac
            return obj

        def hessian(x):
            return basis_matrix.T.dot(basis_matrix)

        lstsq_coef = np.linalg.lstsq(basis_matrix, vals, rcond=0)[0]

        init_guess = np.random.normal(0, 0.1, (true_coef.shape[0], 1))
        #init_guess = lstsq_coef+np.random.normal(0,1e-3,(true_coef.shape[0]))

        errors = check_gradients(objective, True, init_guess, disp=True)
        print(errors.min())
        assert errors.min() < 2e-7

        method = 'trust-constr'
        func = partial(objective, return_jac=True)
        jac = True
        hess = hessian
        options = {
            'gtol': tol,
            'verbose': 0,
            'disp': True,
            'xtol': tol,
            'maxiter': 10000
        }
        constraints = []
        from pyapprox.optimization import \
            PyapproxFunctionAsScipyMinimizeObjective

        fun = PyapproxFunctionAsScipyMinimizeObjective(func)
        res = minimize(fun,
                       init_guess[:, 0],
                       method=method,
                       jac=jac,
                       hess=hess,
                       options=options,
                       constraints=constraints)

        #print(lstsq_coef)
        #print(res.x,true_coef)
        assert np.allclose(res.x[:, np.newaxis], true_coef, atol=1e-4)
Example #7
0
 def test_smooth_max_function_gradients(self):
     smoother_type, eps = 0, 1e-1
     #x = np.linspace(-1,1,101)
     #plt.plot(x,smooth_max_function(smoother_type,eps,x));plt.show()
     #plt.plot(x,smooth_max_function_first_derivative(smoother_type,eps,x));plt.show()
     x = np.array([0.01])
     errors = check_gradients(
         partial(smooth_max_function, smoother_type, eps),
         partial(smooth_max_function_first_derivative, smoother_type, eps),
         x[:, np.newaxis])
     assert errors.min() < 1e-6
    def help_check_smooth_conditional_value_at_risk(self, smoother_type, eps,
                                                    alpha):
        samples = np.linspace(-1, 1, 11)
        t = value_at_risk(samples, alpha)[0]
        x0 = np.hstack((samples, t))[:, None]
        errors = check_gradients(
            lambda xx: smooth_conditional_value_at_risk(
                smoother_type, eps, alpha, xx),
            lambda xx: smooth_conditional_value_at_risk_gradient(
                smoother_type, eps, alpha, xx), x0)
        assert errors.min() < 1e-6

        weights = np.random.uniform(1, 2, samples.shape[0])
        weights /= weights.sum()
        errors = check_gradients(
            lambda xx: smooth_conditional_value_at_risk(
                smoother_type, eps, alpha, xx, weights),
            lambda xx: smooth_conditional_value_at_risk_gradient(
                smoother_type, eps, alpha, xx, weights), x0)
        assert errors.min() < 1e-6
Example #9
0
    def test_smooth_conditional_value_at_risk_composition_gradient(self):
        smoother_type, eps, alpha = 0, 1e-1, 0.7
        nsamples, nvars = 4, 2
        samples = np.arange(nsamples * nvars).reshape(nvars, nsamples)
        t = 0.1
        x0 = np.array([2, 3, t])[:, np.newaxis]
        fun = lambda x: (np.sum((x * samples)**2, axis=0).T)[:, np.newaxis]
        jac = lambda x: 2 * (x * samples**2).T

        errors = check_gradients(fun, jac, x0[:2], disp=False)
        assert (errors.min() < 1e-6)

        #import pyapprox as pya
        #f = lambda x: smooth_conditional_value_at_risk_composition(smoother_type,eps,alpha,fun,jac,x)[0]
        #print(pya.approx_jacobian(f,x0))
        #print(smooth_conditional_value_at_risk_composition(smoother_type,eps,alpha,fun,jac,x0)[1])

        errors = check_gradients(
            partial(smooth_conditional_value_at_risk_composition,
                    smoother_type, eps, alpha, fun, jac), True, x0)
        assert errors.min() < 1e-7
    def test_christoffel_leja_objective_gradients(self):
        #leja_sequence = np.array([[-1, 1]])
        leja_sequence = np.array([[-1, 0, 1]])
        degree = leja_sequence.shape[1] - 1
        ab = jacobi_recurrence(degree + 2, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree + 1,
                            ab=ab)
        tmp = basis_fun(leja_sequence[0, :])
        nterms = degree + 1
        basis_mat = tmp[:, :nterms]
        new_basis = tmp[:, nterms:]
        coef = compute_coefficients_of_christoffel_leja_interpolant_1d(
            basis_mat, new_basis)

        fun = partial(christoffel_leja_objective_fun_1d, basis_fun, coef)

        #xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        #plt.plot(leja_sequence[0, :], fun(leja_sequence), 'o'); plt.show()

        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree + 1,
                                    ab=ab,
                                    deriv_order=1)
        jac = partial(christoffel_leja_objective_jac_1d, basis_fun_and_jac,
                      coef)

        sample = sample = np.random.uniform(-1, 1, (1, 1))
        err = check_gradients(fun, jac, sample)
        assert err.max() > 0.5 and err.min() < 1e-7

        basis_fun_jac_hess = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                     nmax=degree + 1,
                                     ab=ab,
                                     deriv_order=2)
        hess = partial(christoffel_leja_objective_hess_1d, basis_fun_jac_hess,
                       coef)
        err = check_gradients(jac, hess, sample)
        assert err.max() > .5 and err.min() < 1e-7
    def test_smooth_l1_norm_gradients(self):
        #x = np.linspace(-1,1,101)
        #t = np.ones_like(x)
        #r = 1e1
        #plt.plot(x,kouri_smooth_absolute_value(t,r,x))
        #plt.show()

        t = np.ones(5);r=1
        init_guess = np.random.normal(0,1,(t.shape[0]))
        func = partial(kouri_smooth_l1_norm,t,r)
        jac  = partial(kouri_smooth_l1_norm_gradient,t,r)
        errors = check_gradients(func,jac,init_guess,disp=False)
        assert errors.min()<3e-7

        fd_hess = approx_jacobian(jac,init_guess)
        assert np.allclose(fd_hess,kouri_smooth_l1_norm_hessian(t,r,init_guess))
    def check_nn_loss_gradients(self, activation_fun):
        nvars = 3
        nqoi = 2

        opts = {
            'activation_func': activation_fun,
            'layers': [nvars, 3, nqoi],
            'loss_func': 'squared_loss',
            'lag_mult': 0.5
        }
        # No hidden layers works
        # opts = {'activation_func':'sigmoid', 'layers':[nvars, nqoi],
        #        'loss_func':'squared_loss'}
        network = NeuralNetwork(opts)

        # train_samples = np.linspace(0, 1, 11)[None, :]
        train_samples = np.random.uniform(0, 1, (nvars, nvars * 11))
        train_values = np.hstack([
            np.sum(train_samples**(ii + 2), axis=0)[:, None]
            for ii in range(nqoi)
        ])
        obj = partial(network.objective_function, train_samples, train_values)
        jac = partial(network.objective_jacobian, train_samples, train_values)
        parameters = np.random.normal(0, 1, (network.nparams))

        disp = True

        # disp = False

        def fun(x):
            return np.sum(obj(x))

        zz = parameters[:, None]

        errors = check_gradients(fun,
                                 jac,
                                 zz,
                                 plot=False,
                                 disp=disp,
                                 rel=True,
                                 direction=None,
                                 jacp=None)
        # make sure gradient changes by six orders of magnitude
        assert np.log10(errors.max()) - np.log10(errors.min()) > 6
    def check_nn_input_gradients(self, activation_fun):
        nvars = 3
        nqoi = 2

        opts = {
            'activation_func': activation_fun,
            'layers': [nvars, 3, nqoi],
            'loss_func': 'squared_loss'
        }
        # No hidden layers works
        # opts = {'activation_func':'sigmoid', 'layers':[nvars, nqoi],
        #        'loss_func':'squared_loss'}
        network = NeuralNetwork(opts)

        # train_samples = np.linspace(0, 1, 11)[None, :]
        train_samples = np.random.uniform(0, 1, (nvars, nvars * 11))
        train_values = np.hstack([
            np.sum(train_samples**(ii + 2), axis=0)[:, None]
            for ii in range(nqoi)
        ])
        obj = partial(network.objective_function, train_samples, train_values)
        jac = partial(network.objective_jacobian, train_samples, train_values)
        parameters = np.random.normal(0, 1, (network.nparams))

        disp = True
        # disp = False

        jac = partial(network.gradient_wrt_inputs, parameters, store=True)
        x0 = train_samples[:, :1]

        def fun(x):
            return network.forward_propagate(x, parameters).T

        errors = check_gradients(fun,
                                 jac,
                                 x0,
                                 plot=False,
                                 disp=disp,
                                 rel=True,
                                 direction=None,
                                 jacp=None)
        # print(np.log10(errors.max())-np.log10(errors.min()))
        assert np.log10(errors.max()) - np.log10(errors.min()) > 5.7  # 6
Example #14
0
    def test_smooth_l1_norm_gradients(self):
        #x = np.linspace(-1,1,101)
        #t = np.ones_like(x)
        #r = 1e1
        #plt.plot(x,kouri_smooth_absolute_value(t,r,x))
        #plt.show()
        from pyapprox.optimization import \
            ScipyMinimizeObjectiveAsPyapproxFunction,\
            ScipyMinimizeObjectiveJacAsPyapproxJac

        t = np.ones(5)
        r = 1
        init_guess = np.random.normal(0, 1, (t.shape[0], 1))
        func = ScipyMinimizeObjectiveAsPyapproxFunction(
            partial(kouri_smooth_l1_norm, t, r))
        jac = partial(kouri_smooth_l1_norm_gradient, t, r)
        pya_jac = ScipyMinimizeObjectiveJacAsPyapproxJac(jac)
        errors = check_gradients(func, pya_jac, init_guess, disp=False)
        assert errors.min() < 3e-7

        fd_hess = approx_jacobian(jac, init_guess[:, 0])
        assert np.allclose(fd_hess,
                           kouri_smooth_l1_norm_hessian(t, r, init_guess))
    def test_pdf_weighted_leja_objective_gradients(self):
        #leja_sequence = np.array([[-1, 1]])
        leja_sequence = np.array([[-1, 0, 1]])
        degree = leja_sequence.shape[1] - 1
        ab = jacobi_recurrence(degree + 2, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree + 1,
                            ab=ab)

        def pdf(x):
            return beta_pdf(1, 1, (x + 1) / 2) / 2

        def pdf_jac(x):
            return beta_pdf_derivative(1, 1, (x + 1) / 2) / 4

        tmp = basis_fun(leja_sequence[0, :])
        nterms = degree + 1
        basis_mat = tmp[:, :nterms]
        new_basis = tmp[:, nterms:]
        coef = compute_coefficients_of_pdf_weighted_leja_interpolant_1d(
            pdf(leja_sequence[0, :]), basis_mat, new_basis)

        fun = partial(pdf_weighted_leja_objective_fun_1d, pdf, basis_fun, coef)

        #xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        #plt.plot(leja_sequence[0, :], fun(leja_sequence), 'o'); plt.show()

        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree + 1,
                                    ab=ab,
                                    deriv_order=1)
        jac = partial(pdf_weighted_leja_objective_jac_1d, pdf, pdf_jac,
                      basis_fun_and_jac, coef)

        sample = sample = np.random.uniform(-1, 1, (1, 1))
        err = check_gradients(fun, jac, sample)
        assert err.max() > 0.4 and err.min() < 1e-7
Example #16
0
    def test_advection_diffusion_base_class_adjoint(self):
        nvars, corr_len = 2, 0.1
        benchmark = setup_advection_diffusion_benchmark(nvars=nvars,
                                                        corr_len=corr_len,
                                                        max_eval_concurrency=1)
        model = benchmark.fun
        #random_samples = np.zeros((nvars,1))
        random_samples = -np.sqrt(3) * np.ones((nvars, 1))
        config_samples = 3 * np.ones((3, 1))
        samples = np.vstack([random_samples, config_samples])
        bmodel = model.base_model
        qoi = bmodel(samples)
        assert np.all(np.isfinite(qoi))
        sol = bmodel.solve(samples)

        grad = qoi_functional_grad_misc(sol, bmodel)

        kappa = bmodel.kappa
        J = dl_qoi_functional_misc(sol)
        control = dla.Control(kappa)
        Jhat = dla.ReducedFunctional(J, control)
        h = dla.Function(kappa.function_space())
        h.vector()[:] = np.random.normal(0, 1, kappa.function_space().dim())
        conv_rate = dla.taylor_test(Jhat, kappa, h)
        assert np.allclose(conv_rate, 2.0, atol=1e-3)

        # Check that gradient with respect to kappa is calculated correctly
        # this requires passing in entire kappa vector and not just variables
        # used to compute the KLE.
        from pyapprox.optimization import check_gradients
        from pyapprox.models.wrappers import SingleFidelityWrapper
        from functools import partial

        init_condition, boundary_conditions, function_space, beta, \
            forcing, kappa = bmodel.initialize_random_expressions(
                random_samples[:, 0])

        def fun(np_kappa):
            dt = 0.1
            fn_kappa = dla.Function(function_space)
            fn_kappa.vector()[:] = np_kappa[:, 0]
            bmodel.kappa = fn_kappa
            sol = run_model(
                function_space,
                fn_kappa,
                forcing,
                init_condition,
                dt,
                bmodel.final_time,
                boundary_conditions,
                velocity=beta,
                second_order_timestepping=bmodel.second_order_timestepping,
                intermediate_times=bmodel.options.get('intermediate_times',
                                                      None))
            vals = np.atleast_1d(bmodel.qoi_functional(sol))
            if vals.ndim == 1:
                vals = vals[:, np.newaxis]
            grad = bmodel.qoi_functional_grad(sol, bmodel)
            return vals, grad

        kappa = bmodel.get_diffusivity(random_samples[:, 0])
        x0 = dla.project(kappa, function_space).vector()[:].copy()[:, None]
        check_gradients(fun, True, x0)

        # Test that gradient with respect to kle coefficients is correct

        from pyapprox.karhunen_loeve_expansion import \
            compute_kle_gradient_from_mesh_gradient
        vals, jac = bmodel(samples, jac=True)

        # Extract mean field and KLE basis from expression
        # TODO add ability to compute gradient of kle to
        # nobile_diffusivity_fenics_class
        kle = bmodel.get_diffusivity(np.zeros(random_samples.shape[0]))
        mean_field_fn = dla.Function(function_space)
        mean_field_fn = dla.interpolate(kle, function_space)
        mean_field = mean_field_fn.vector()[:].copy() - np.exp(1)

        mesh_coords = function_space.tabulate_dof_coordinates()[:, 0]
        I = np.argsort(mesh_coords)
        basis_matrix = np.empty((mean_field.shape[0], random_samples.shape[0]))
        exact_basis_matrix = np.array([
            mesh_coords * 0 + 1,
            np.sin((2) / 2 * np.pi * mesh_coords / bmodel.options['corr_len'])
        ]).T
        for ii in range(random_samples.shape[0]):
            zz = np.zeros(random_samples.shape[0])
            zz[ii] = 1.0
            kle = bmodel.get_diffusivity(zz)
            field_fn = dla.Function(function_space)
            field_fn = dla.interpolate(kle, function_space)
            # 1e-15 used to avoid taking log of zero
            basis_matrix[:, ii] = np.log(field_fn.vector()[:].copy() -
                                         mean_field + 1e-15) - 1

        assert np.allclose(
            mean_field + np.exp(1 + basis_matrix.dot(random_samples[:, 0])),
            x0[:, 0])

        # nobile diffusivity uses different definitions of KLE
        # k = np.exp(1+basis_matrix.dot(coef))+mean_field
        # than that assumed in compute_kle_gradient_from_mesh_gradient
        # k = np.exp(basis_matrix.dot(coef)+mean_field)
        # So to
        # keep current interface set mean field to zero and then correct
        # returned gradient
        grad = compute_kle_gradient_from_mesh_gradient(jac, basis_matrix,
                                                       mean_field * 0, True,
                                                       random_samples[:, 0])
        grad *= np.exp(1)

        from pyapprox.optimization import approx_jacobian
        fun = SingleFidelityWrapper(bmodel, config_samples[:, 0])
        fd_grad = approx_jacobian(fun, random_samples)

        # print(grad, fd_grad)
        assert np.allclose(grad, fd_grad)
    def test_adjoint(self):

        np_kappa = 2
        nx, ny, degree = 11, 11, 2
        mesh = dla.RectangleMesh(dl.Point(0, 0), dl.Point(1, 1), nx, ny)
        function_space = dl.FunctionSpace(mesh, "Lagrange", degree)

        def constant_diffusion(kappa, u):
            return kappa

        boundary_conditions = \
            get_dirichlet_boundary_conditions_from_expression(
                get_advec_exact_solution(mesh, degree), 0, 1, 0, 1)

        class NonlinearDiffusivity(object):
            def __init__(self, kappa):
                self.kappa = kappa

            def __call__(self, u):
                return (self.kappa+u**2)

        dl_kappa = dla.Constant(np_kappa)
        options = {'time_step': 0.05, 'final_time': 1.,
                   'forcing': dla.Constant(1),
                   'boundary_conditions': boundary_conditions,
                   'init_condition': get_advec_exact_solution(mesh, degree),
                   'nonlinear_diffusion': NonlinearDiffusivity(dl_kappa),
                   'second_order_timestepping': True,
                   'nlsparam': dict()}

        def dl_qoi_functional(sol):
            return dla.assemble(sol*dl.dx)

        def dl_fun(np_kappa):
            kappa = dla.Constant(np_kappa)
            options_copy = options.copy()
            options_copy['forcing'] = dla.Constant(1.0)
            # using class avoids pickling
            options_copy['nonlinear_diffusion'] = NonlinearDiffusivity(kappa)
            sol = run_model(function_space, **options_copy)
            return sol, kappa

        def fun(np_kappa):
            np_kappa = np_kappa[0, 0]
            sol, kappa = dl_fun(np_kappa)
            J = dl_qoi_functional(sol)
            control = dla.Control(kappa)
            dJd_kappa = dla.compute_gradient(J, [control])[0]
            return np.atleast_1d(float(J)), np.atleast_2d(float(dJd_kappa))

        sol, kappa = dl_fun(np_kappa)

        J = dl_qoi_functional(sol)
        control = dla.Control(kappa)
        Jhat = dla.ReducedFunctional(J, control)
        # h = dla.Constant(np.random.normal(0, 1, 1))
        # conv_rate = dla.taylor_test(Jhat, kappa, h)
        # assert np.allclose(conv_rate, 2.0, atol=1e-2)

        from pyapprox.optimization import check_gradients
        x0 = np.atleast_2d(np_kappa)
        errors = check_gradients(fun, True, x0)
        assert errors.min() < 1e-7 and errors.max() > 1e-1
    def test_halfar_model(self):
        nlsparams = get_default_snes_nlsparams()
        # nlsparams = get_default_newton_nlsparams()

        def dl_qoi_functional(sol):
            return dla.assemble(sol*dl.dx)

        def qoi_functional(sol):
            return np.atleast_1d(float(dl_qoi_functional(sol)))

        def qoi_functional_grad(sol, model):
            J = dl_qoi_functional(sol)
            control = dla.Control(model.shallow_ice_diffusivity.Gamma)
            dJd_gamma = dla.compute_gradient(J, [control])[0]
            # apply chain rule. we want gradient of qoi as a function of x
            # but fenics compute gradient with respect to g(x)=(1+x)*Gamma
            # dq/dx = dq/dg*dg/dx
            dJd_gamma *= model.shallow_ice_diffusivity.Gamma
            # h = dla.Constant(1e-5) # h must be similar magnitude to Gamma
            #Jhat = dla.ReducedFunctional(J, control)
            # conv_rate = dla.taylor_test(
            #    Jhat, model.shallow_ice_diffusivity.Gamma, h)
            return np.atleast_2d(float(dJd_gamma))

        if not has_dla:
            qoi_functional_grad = None

        secpera = 31556926  # seconds per anum
        init_time = 200*secpera
        final_time, degree, nphys_dim = 300*secpera, 1, 1  # 600*secpera, 1, 1
        model = HalfarShallowIceModel(
            nphys_dim, init_time, final_time, degree, qoi_functional,
            second_order_timestepping=True, nlsparams=nlsparams,
            qoi_functional_grad=qoi_functional_grad)

        # for nphys_dim=1 [8, 8] will produce error of 2.7 e-5
        # but stagnates for a while at around 1e-4 for values
        # 5, 6, 7
        random_sample = np.array([[0]]).T
        config_sample = np.array([[4]*nphys_dim + [4]]).T
        sample = np.vstack((random_sample, config_sample))
        sol = model.solve(sample)

        exact_solution = get_halfar_shallow_ice_exact_solution(
            model.Gamma, model.mesh, model.degree, model.nphys_dim)
        exact_solution.t = final_time
        error = dl.errornorm(exact_solution, sol, mesh=model.mesh)
        print('Abs. Error', error)
        rel_error = error/dl.sqrt(
            dla.assemble(exact_solution**2*dl.dx(degree=5)))
        print('Rel. Error', rel_error)

        assert rel_error < 1e-3

        if not has_dla:
            return
        # TODO: complete test qoi grad but first add taylor_test
        val, grad = model(sample, True)
        print(val, grad)

        from pyapprox.optimization import check_gradients
        from pyapprox.models.wrappers import SingleFidelityWrapper
        fun = SingleFidelityWrapper(
            partial(model, jac=True), config_sample[:, 0])
        x0 = np.atleast_2d(model.Gamma)
        errors = check_gradients(fun, True, x0, direction=np.atleast_2d(1))
        assert errors.min() < 3e-5 and errors.max() > 1e-1