Пример #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_global_fitting(self):
        """
        Test a global fitting scenario with datasets of unequal length. In this
        scenario, a quartic equation is fitted where the constant term is shared
        between the datasets. (e.g. identical background noise)
        """
        x_1, x_2, y_1, y_2 = variables('x_1, x_2, y_1, y_2')
        y0, a_1, a_2, b_1, b_2 = parameters('y0, a_1, a_2, b_1, b_2')

        # The following vector valued function links all the equations together
        # as stated in the intro.
        model = Model({
            y_1: a_1 * x_1**2 + b_1 * x_1 + y0,
            y_2: a_2 * x_2**2 + b_2 * x_2 + y0,
        })

        # Generate data from this model
        # xdata = np.linspace(0, 10)
        xdata1 = np.linspace(0, 10)
        xdata2 = xdata1[::2]  # Make the sets of unequal size

        ydata1, ydata2 = model(x_1=xdata1, x_2=xdata2, a_1=101.3, b_1=0.5, a_2=56.3, b_2=1.1111, y0=10.8)
        # Add some noise to make it appear like real data
        np.random.seed(1)
        ydata1 += np.random.normal(0, 2, size=ydata1.shape)
        ydata2 += np.random.normal(0, 2, size=ydata2.shape)

        xdata = [xdata1, xdata2]
        ydata = [ydata1, ydata2]

        # Guesses
        a_1.value = 100
        a_2.value = 50
        b_1.value = 1
        b_2.value = 1
        y0.value = 10

        eval_jac = model.eval_jacobian(x_1=xdata1, x_2=xdata2, a_1=101.3,
                                       b_1=0.5, a_2=56.3, b_2=1.1111, y0=10.8)
        self.assertEqual(len(eval_jac), 2)
        for comp in eval_jac:
            self.assertEqual(len(comp), len(model.params))

        sigma_y = np.concatenate((np.ones(20), [2., 4., 5, 7, 3]))

        fit = Fit(model, x_1=xdata[0], x_2=xdata[1],
                  y_1=ydata[0], y_2=ydata[1], sigma_y_2=sigma_y)
        fit_result = fit.execute()

        # fit_curves = model(x_1=xdata[0], x_2=xdata[1], **fit_result.params)
        self.assertAlmostEqual(fit_result.value(y0), 1.061892e+01, 3)
        self.assertAlmostEqual(fit_result.value(a_1), 1.013269e+02, 3)
        self.assertAlmostEqual(fit_result.value(a_2), 5.625694e+01, 3)
        self.assertAlmostEqual(fit_result.value(b_1), 3.362240e-01, 3)
        self.assertAlmostEqual(fit_result.value(b_2), 1.565253e+00, 3)
Пример #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():
    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__
Пример #5
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__)
Пример #6
0
def test_global_fitting():
    """
    Test a global fitting scenario with datasets of unequal length. In this
    scenario, a quartic equation is fitted where the constant term is shared
    between the datasets. (e.g. identical background noise)
    """
    x_1, x_2, y_1, y_2 = variables('x_1, x_2, y_1, y_2')
    y0, a_1, a_2, b_1, b_2 = parameters('y0, a_1, a_2, b_1, b_2')

    # The following vector valued function links all the equations together
    # as stated in the intro.
    model = Model({
        y_1: a_1 * x_1**2 + b_1 * x_1 + y0,
        y_2: a_2 * x_2**2 + b_2 * x_2 + y0,
    })

    # Generate data from this model
    # xdata = np.linspace(0, 10)
    xdata1 = np.linspace(0, 10)
    xdata2 = xdata1[::2]  # Make the sets of unequal size

    ydata1, ydata2 = model(x_1=xdata1,
                           x_2=xdata2,
                           a_1=101.3,
                           b_1=0.5,
                           a_2=56.3,
                           b_2=1.1111,
                           y0=10.8)
    # Add some noise to make it appear like real data
    np.random.seed(1)
    ydata1 += np.random.normal(0, 2, size=ydata1.shape)
    ydata2 += np.random.normal(0, 2, size=ydata2.shape)

    xdata = [xdata1, xdata2]
    ydata = [ydata1, ydata2]

    # Guesses
    a_1.value = 100
    a_2.value = 50
    b_1.value = 1
    b_2.value = 1
    y0.value = 10

    eval_jac = model.eval_jacobian(x_1=xdata1,
                                   x_2=xdata2,
                                   a_1=101.3,
                                   b_1=0.5,
                                   a_2=56.3,
                                   b_2=1.1111,
                                   y0=10.8)
    assert len(eval_jac) == 2
    for comp in eval_jac:
        assert len(comp) == len(model.params)

    sigma_y = np.concatenate((np.ones(20), [2., 4., 5, 7, 3]))

    fit = Fit(model,
              x_1=xdata[0],
              x_2=xdata[1],
              y_1=ydata[0],
              y_2=ydata[1],
              sigma_y_2=sigma_y)
    fit_result = fit.execute()

    # fit_curves = model(x_1=xdata[0], x_2=xdata[1], **fit_result.params)
    assert fit_result.value(y0) == pytest.approx(1.061892e+01, 1e-03)
    assert fit_result.value(a_1) == pytest.approx(1.013269e+02, 1e-03)
    assert fit_result.value(a_2) == pytest.approx(5.625694e+01, 1e-03)
    assert fit_result.value(b_1) == pytest.approx(3.362240e-01, 1e-03)
    assert fit_result.value(b_2) == pytest.approx(1.565253e+00, 1e-03)