Пример #1
0
def test_LeastSquares():
    """
    Tests if the LeastSquares objective gives the right shapes of output by
    comparing with its analytical equivalent.
    """
    i = Idx('i', 100)
    x, y = symbols('x, y', cls=Variable)
    X2 = symbols('X2', cls=Variable)
    a, b = parameters('a, b')

    model = Model({y: a * x**2 + b * x})
    xdata = np.linspace(0, 10, 100)
    ydata = model(x=xdata, a=5, b=2).y + np.random.normal(0, 5, xdata.shape)

    # Construct a LeastSquares objective and its analytical equivalent
    chi2_numerical = LeastSquares(model,
                                  data={
                                      x: xdata,
                                      y: ydata,
                                      model.sigmas[y]: np.ones_like(xdata)
                                  })
    chi2_exact = Model({X2: FlattenSum(0.5 * ((a * x**2 + b * x) - y)**2, i)})

    eval_exact = chi2_exact(x=xdata, y=ydata, a=2, b=3)
    jac_exact = chi2_exact.eval_jacobian(x=xdata, y=ydata, a=2, b=3)
    hess_exact = chi2_exact.eval_hessian(x=xdata, y=ydata, a=2, b=3)
    eval_numerical = chi2_numerical(x=xdata, a=2, b=3)
    jac_numerical = chi2_numerical.eval_jacobian(x=xdata, a=2, b=3)
    hess_numerical = chi2_numerical.eval_hessian(x=xdata, a=2, b=3)

    # Test model jacobian and hessian shape
    assert model(x=xdata, a=2, b=3)[0].shape == ydata.shape
    assert model.eval_jacobian(x=xdata, a=2, b=3)[0].shape == (2, 100)
    assert model.eval_hessian(x=xdata, a=2, b=3)[0].shape == (2, 2, 100)
    # Test exact chi2 shape
    assert eval_exact[0].shape, (1, )
    assert jac_exact[0].shape, (2, 1)
    assert hess_exact[0].shape, (2, 2, 1)

    # Test if these two models have the same call, jacobian, and hessian
    assert eval_exact[0] == pytest.approx(eval_numerical)
    assert isinstance(eval_numerical, float)
    assert isinstance(eval_exact[0][0], float)
    assert np.squeeze(jac_exact[0], axis=-1) == pytest.approx(jac_numerical)
    assert isinstance(jac_numerical, np.ndarray)
    assert np.squeeze(hess_exact[0], axis=-1) == pytest.approx(hess_numerical)
    assert isinstance(hess_numerical, np.ndarray)

    fit = Fit(chi2_exact, x=xdata, y=ydata, objective=MinimizeModel)
    fit_exact_result = fit.execute()
    fit = Fit(model, x=xdata, y=ydata, absolute_sigma=True)
    fit_num_result = fit.execute()
    assert fit_exact_result.value(a) == fit_num_result.value(a)
    assert fit_exact_result.value(b) == fit_num_result.value(b)
    assert fit_exact_result.stdev(a) == pytest.approx(fit_num_result.stdev(a))
    assert fit_exact_result.stdev(b) == pytest.approx(fit_num_result.stdev(b))
Пример #2
0
def test_interdependency():
    a, b = parameters('a, b')
    x, y, z = variables('x, y, z')
    model_dict = {
        y: a**3 * x + b**2,
        z: y**2 + a * b
    }
    callable_model = CallableModel(model_dict)
    assert callable_model.independent_vars == [x]
    assert callable_model.interdependent_vars == [y]
    assert callable_model.dependent_vars == [z]
    assert callable_model.params == [a, b]
    assert callable_model.connectivity_mapping == {y: {a, b, x}, z: {a, b, y}}
    assert callable_model(x=3, a=1, b=2) == pytest.approx(np.atleast_2d([7, 51]).T)
    for var, func in callable_model.vars_as_functions.items():
        # TODO comment on what this does
        str_con_map = set(x.name for x in callable_model.connectivity_mapping[var])
        str_args = set(str(x.__class__) if isinstance(x, Function) else x.name
                       for x in func.args)
        assert str_con_map == str_args

    jac_model = jacobian_from_model(callable_model)
    assert jac_model.params == [a, b]
    assert jac_model.dependent_vars == [D(z, a), D(z, b), z]
    assert jac_model.interdependent_vars == [D(y, a), D(y, b), y]
    assert jac_model.independent_vars == [x]
    for p1, p2 in zip_longest(jac_model.__signature__.parameters, [x, a, b]):
        assert str(p1) == str(p2)
    # The connectivity of jac_model should be that from it's own components
    # plus that of the model. The latter is needed to properly compute the
    # Hessian.
    jac_con_map = {D(y, a): {a, x},
                   D(y, b): {b},
                   D(z, a): {b, y, D(y, a)},
                   D(z, b): {a, y, D(y, b)},
                   y: {a, b, x}, z: {a, b, y}}
    assert jac_model.connectivity_mapping == jac_con_map
    jac_model_dict = {D(y, a): 3 * a**2 * x,
                      D(y, b): 2 * b,
                      D(z, a): b + 2 * y * D(y, a),
                      D(z, b): a + 2 * y * D(y, b),
                      y: callable_model[y], z: callable_model[z]}
    assert jac_model.model_dict == jac_model_dict
    for var, func in jac_model.vars_as_functions.items():
        str_con_map = set(x.name for x in jac_model.connectivity_mapping[var])
        str_args = set(str(x.__class__) if isinstance(x, Function) else x.name
                       for x in func.args)
        assert str_con_map == str_args

    hess_model = hessian_from_model(callable_model)
    # Result according to Mathematica
    hess_as_dict = {
        D(y, (a, 2)): 6 * a * x,
        D(y, a, b): 0,
        D(y, b, a): 0,
        D(y, (b, 2)): 2,
        D(z, (a, 2)): 2 * D(y, a)**2 + 2 * y * D(y, (a, 2)),
        D(z, a, b): 1 + 2 * D(y, b) * D(y, a) + 2 * y * D(y, a, b),
        D(z, b, a): 1 + 2 * D(y, b) * D(y, a) + 2 * y * D(y, a, b),
        D(z, (b, 2)): 2 * D(y, b)**2 + 2 * y * D(y, (b, 2)),
        D(y, a): 3 * a ** 2 * x,
        D(y, b): 2 * b,
        D(z, a): b + 2 * y * D(y, a),
        D(z, b): a + 2 * y * D(y, b),
        y: callable_model[y], z: callable_model[z]
    }
    assert dict(hess_model) == hess_as_dict

    assert hess_model.params == [a, b]
    assert hess_model.dependent_vars == [D(z, (a, 2)), D(z, a, b), D(z, (b, 2)), D(z, b, a), D(z, a), D(z, b), z]
    assert hess_model.interdependent_vars == [D(y, (a, 2)), D(y, a), D(y, b), y]
    assert hess_model.independent_vars == [x]

    model = Model(model_dict)
    assert model(x=3, a=1, b=2) == pytest.approx(np.atleast_2d([7, 51]).T)
    assert model.eval_jacobian(x=3, a=1, b=2) == pytest.approx(np.array([[[9], [4]], [[128], [57]]]))
    assert model.eval_hessian(x=3, a=1, b=2) == pytest.approx(np.array([[[[18], [0]], [[0], [2]]],[[[414], [73]], [[73], [60]]]]))

    assert model.__signature__ == model.jacobian_model.__signature__
    assert model.__signature__ == model.hessian_model.__signature__
Пример #3
0
def test_LogLikelihood():
    """
    Tests if the LeastSquares objective gives the right shapes of output by
    comparing with its analytical equivalent.
    """
    # TODO: update these tests to use indexed variables in the future
    a, b = parameters('a, b')
    i = Idx('i', 100)
    x, y = variables('x, y')
    pdf = Exp(x, 1 / a) * Exp(x, b)

    np.random.seed(10)
    xdata = np.random.exponential(3.5, 100)

    # We use minus loglikelihood for the model, because the objective was
    # designed to find the maximum when used with a *minimizer*, so it has
    # opposite sign. Also test MinimizeModel at the same time.
    logL_model = Model({y: pdf})
    logL_exact = Model({y: -FlattenSum(log(pdf), i)})
    logL_numerical = LogLikelihood(logL_model, {x: xdata, y: None})
    logL_minmodel = MinimizeModel(logL_exact, data={x: xdata, y: None})

    # Test model jacobian and hessian shape
    eval_exact = logL_exact(x=xdata, a=2, b=3)
    jac_exact = logL_exact.eval_jacobian(x=xdata, a=2, b=3)
    hess_exact = logL_exact.eval_hessian(x=xdata, a=2, b=3)
    eval_minimizemodel = logL_minmodel(a=2, b=3)
    jac_minimizemodel = logL_minmodel.eval_jacobian(a=2, b=3)
    hess_minimizemodel = logL_minmodel.eval_hessian(a=2, b=3)
    eval_numerical = logL_numerical(a=2, b=3)
    jac_numerical = logL_numerical.eval_jacobian(a=2, b=3)
    hess_numerical = logL_numerical.eval_hessian(a=2, b=3)

    # TODO: These shapes should not have the ones! This is due to the current
    # convention that scalars should be returned as a 1d array by Model's.
    assert eval_exact[0].shape == (1, )
    assert jac_exact[0].shape == (2, 1)
    assert hess_exact[0].shape == (2, 2, 1)
    # Test if identical to MinimizeModel
    assert eval_exact[0] == pytest.approx(eval_minimizemodel)
    assert jac_exact[0] == pytest.approx(jac_minimizemodel)
    assert hess_exact[0] == pytest.approx(hess_minimizemodel)

    # Test if these two models have the same call, jacobian, and hessian.
    # Since models always have components as their first dimension, we have
    # to slice that away.
    assert eval_exact.y == pytest.approx(eval_numerical)
    assert isinstance(eval_numerical, float)
    assert isinstance(eval_exact.y[0], float)
    assert np.squeeze(jac_exact[0], axis=-1) == pytest.approx(jac_numerical)
    assert isinstance(jac_numerical, np.ndarray)
    assert np.squeeze(hess_exact[0], axis=-1) == pytest.approx(hess_numerical)
    assert isinstance(hess_numerical, np.ndarray)

    fit = Fit(logL_exact, x=xdata, objective=MinimizeModel)
    fit_exact_result = fit.execute()
    fit = Fit(logL_model, x=xdata, objective=LogLikelihood)
    fit_num_result = fit.execute()
    assert fit_exact_result.value(a) == pytest.approx(fit_num_result.value(a))
    assert fit_exact_result.value(b) == pytest.approx(fit_num_result.value(b))
    assert fit_exact_result.stdev(a) == pytest.approx(fit_num_result.stdev(a))
    assert fit_exact_result.stdev(b) == pytest.approx(fit_num_result.stdev(b))
Пример #4
0
    def test_interdependency(self):
        a, b = parameters('a, b')
        x, y, z = variables('x, y, z')
        model_dict = {
            y: a**3 * x + b**2,
            z: y**2 + a * b
        }
        callable_model = CallableModel(model_dict)
        self.assertEqual(callable_model.independent_vars, [x])
        self.assertEqual(callable_model.interdependent_vars, [y])
        self.assertEqual(callable_model.dependent_vars, [z])
        self.assertEqual(callable_model.params, [a, b])
        self.assertEqual(callable_model.connectivity_mapping,
                         {y: {a, b, x}, z: {a, b, y}})
        np.testing.assert_almost_equal(callable_model(x=3, a=1, b=2),
                                       np.atleast_2d([7, 51]).T)
        for var, func in callable_model.vars_as_functions.items():
            self.assertEqual(
                set(str(x) for x in callable_model.connectivity_mapping[var]),
                set(str(x.__class__) if isinstance(x, Function) else str(x)
                    for x in func.args)
            )

        jac_model = jacobian_from_model(callable_model)
        self.assertEqual(jac_model.params, [a, b])
        self.assertEqual(jac_model.dependent_vars, [D(z, a), D(z, b), z])
        self.assertEqual(jac_model.interdependent_vars, [D(y, a), D(y, b), y])
        self.assertEqual(jac_model.independent_vars, [x])
        for p1, p2 in zip_longest(jac_model.__signature__.parameters, [x, a, b]):
            self.assertEqual(str(p1), str(p2))
        # The connectivity of jac_model should be that from it's own components
        # plus that of the model. The latter is needed to properly compute the
        # Hessian.
        self.assertEqual(
            jac_model.connectivity_mapping,
             {D(y, a): {a, x},
              D(y, b): {b},
              D(z, a): {b, y, D(y, a)},
              D(z, b): {a, y, D(y, b)},
              y: {a, b, x}, z: {a, b, y}
              }
        )
        self.assertEqual(
            jac_model.model_dict,
            {D(y, a): 3 * a**2 * x,
             D(y, b): 2 * b,
             D(z, a): b + 2 * y * D(y, a),
             D(z, b): a + 2 * y * D(y, b),
             y: callable_model[y], z: callable_model[z]
             }
        )
        for var, func in jac_model.vars_as_functions.items():
            self.assertEqual(
                set(x.name for x in jac_model.connectivity_mapping[var]),
                set(str(x.__class__) if isinstance(x, Function) else str(x)
                    for x in func.args)
            )
        hess_model = hessian_from_model(callable_model)
        # Result according to Mathematica
        hess_as_dict = {
            D(y, (a, 2)): 6 * a * x,
            D(y, a, b): 0,
            D(y, b, a): 0,
            D(y, (b, 2)): 2,
            D(z, (a, 2)): 2 * D(y, a)**2 + 2 * y * D(y, (a, 2)),
            D(z, a, b): 1 + 2 * D(y, b) * D(y, a) + 2 * y * D(y, a, b),
            D(z, b, a): 1 + 2 * D(y, b) * D(y, a) + 2 * y * D(y, a, b),
            D(z, (b, 2)): 2 * D(y, b)**2 + 2 * y * D(y, (b, 2)),
            D(y, a): 3 * a ** 2 * x,
            D(y, b): 2 * b,
            D(z, a): b + 2 * y * D(y, a),
            D(z, b): a + 2 * y * D(y, b),
            y: callable_model[y], z: callable_model[z]
        }
        self.assertEqual(len(hess_model), len(hess_as_dict))
        for key, expr in hess_model.items():
            self.assertEqual(expr, hess_as_dict[key])

        self.assertEqual(hess_model.params, [a, b])
        self.assertEqual(
            hess_model.dependent_vars,
            [D(z, (a, 2)), D(z, a, b), D(z, (b, 2)), D(z, b, a),
             D(z, a), D(z, b), z]
        )
        self.assertEqual(hess_model.interdependent_vars,
                         [D(y, (a, 2)), D(y, a), D(y, b), y])
        self.assertEqual(hess_model.independent_vars, [x])


        model = Model(model_dict)
        np.testing.assert_almost_equal(model(x=3, a=1, b=2),
                                       np.atleast_2d([7, 51]).T)
        np.testing.assert_almost_equal(model.eval_jacobian(x=3, a=1, b=2),
                                       np.array([[[9], [4]], [[128], [57]]]))
        np.testing.assert_almost_equal(
            model.eval_hessian(x=3, a=1, b=2),
            np.array([[[[18], [0]], [[0], [2]]],
                           [[[414], [73]], [[73], [60]]]]))

        self.assertEqual(model.__signature__, model.jacobian_model.__signature__)
        self.assertEqual(model.__signature__, model.hessian_model.__signature__)