Пример #1
0
def test_pickle():
    """
    Make sure models can be pickled are preserved when pickling
    """
    a, b = parameters('a, b')
    x, y = variables('x, y')
    exact_model = Model({y: a * x ** b})
    constraint = Model.as_constraint(Eq(a, b), exact_model)
    num_model = CallableNumericalModel(
        {y: a * x ** b}, independent_vars=[x], params=[a, b]
    )
    connected_num_model = CallableNumericalModel(
        {y: a * x ** b}, connectivity_mapping={y: {x, a, b}}
    )
    # Test if lsoda args and kwargs are pickled too
    ode_model = ODEModel({D(y, x): a * x + b}, {x: 0.0}, 3, 4, some_kwarg=True)

    models = [exact_model, constraint, num_model, ode_model, connected_num_model]
    for model in models:
        new_model = pickle.loads(pickle.dumps(model))
        # Compare signatures
        assert model.__signature__ == new_model.__signature__
        # Trigger the cached vars because we compare `__dict__` s
        model.vars
        new_model.vars
        # Explicitly make sure the connectivity mapping is identical.
        assert model.connectivity_mapping == new_model.connectivity_mapping
        if not isinstance(model, ODEModel):
            model.function_dict
            model.vars_as_functions
            new_model.function_dict
            new_model.vars_as_functions
        assert model.__dict__ == new_model.__dict__
Пример #2
0
    def test_data_for_constraint(self):
        """
        Test the signature handling when constraints are at play. Constraints
        should take seperate data, but still kwargs that are not found in either
        the model nor the constraints should raise an error.
        """
        A, mu, sig = parameters('A, mu, sig')
        x, y, Y = variables('x, y, Y')

        model = Model({y: A * Gaussian(x, mu=mu, sig=sig)})
        constraint = Model.as_constraint(Y, model, constraint_type=Eq)

        np.random.seed(2)
        xdata = np.random.normal(1.2, 2, 10)
        ydata, xedges = np.histogram(xdata, bins=int(np.sqrt(len(xdata))),
                                     density=True)

        # Allowed
        fit = Fit(model, x=xdata, y=ydata, Y=2, constraints=[constraint])
        fit = Fit(model, x=xdata, y=ydata)
        fit = Fit(model, x=xdata, objective=LogLikelihood)

        # Not allowed
        with self.assertRaises(TypeError):
            fit = Fit(model, x=xdata, y=ydata, Y=2)
        with self.assertRaises(TypeError):
            fit = Fit(model, x=xdata, y=ydata, Y=2, Z=3, constraints=[constraint])
Пример #3
0
    def test_pickle(self):
        """
        Make sure models can be pickled are preserved when pickling
        """
        a, b = parameters('a, b')
        x, y = variables('x, y')
        exact_model = Model({y: a * x ** b})
        constraint = Model.as_constraint(Eq(a, b), exact_model)
        num_model = CallableNumericalModel(
            {y: a * x ** b}, independent_vars=[x], params=[a, b]
        )
        connected_num_model = CallableNumericalModel(
            {y: a * x ** b}, connectivity_mapping={y: {x, a, b}}
        )
        # Test if lsoda args and kwargs are pickled too
        ode_model = ODEModel({D(y, x): a * x + b}, {x: 0.0}, 3, 4, some_kwarg=True)

        models = [exact_model, constraint, num_model, ode_model,
                  connected_num_model]
        for model in models:
            new_model = pickle.loads(pickle.dumps(model))
            # Compare signatures
            self.assertEqual(model.__signature__, new_model.__signature__)
            # Trigger the cached vars because we compare `__dict__` s
            model.vars
            new_model.vars
            # Explicitly make sure the connectivity mapping is identical.
            self.assertEqual(model.connectivity_mapping,
                             new_model.connectivity_mapping)
            if not isinstance(model, ODEModel):
                model.function_dict
                model.vars_as_functions
                new_model.function_dict
                new_model.vars_as_functions
            self.assertEqual(model.__dict__, new_model.__dict__)
Пример #4
0
    def test_minimizer_constraint_compatibility(self):
        """
        Test if #156 has been solved, and test all the other constraint styles.
        """
        x, y, z = variables('x, y, z')
        a, b, c = parameters('a, b, c')
        b.fixed = True

        model = Model({z: a * x**2 - b * y**2 + c})
        # Generate data, z has to be scalar for MinimizeModel to be happy
        xdata = 3 #np.linspace(0, 10)
        ydata = 5 # np.linspace(0, 10)
        zdata = model(a=2, b=3, c=5, x=xdata, y=ydata).z
        data_dict = {x: xdata, y: ydata, z: zdata}

        # Equivalent ways of defining the same constraint
        constraint_model = Model.as_constraint(a - c, model, constraint_type=Eq)
        constraint_model.params = model.params
        constraints = [
            Eq(a, c),
            MinimizeModel(constraint_model, data=data_dict),
            constraint_model
        ]

        objective = MinimizeModel(model, data=data_dict)
        for constraint in constraints:
            fit = SLSQP(objective, parameters=[a, b, c],
                        constraints=[constraint])
            wrapped_constr = fit.wrapped_constraints[0]['fun'].model
            self.assertIsInstance(wrapped_constr, Model)
            self.assertEqual(wrapped_constr.params, model.params)
            self.assertEqual(wrapped_constr.jacobian_model.params, model.params)
            self.assertEqual(wrapped_constr.hessian_model.params, model.params)
            # Set the data for the dependent var of the constraint to None
            # Normally this is handled by Fit because here we interact with the
            # Minimizer directly, it is up to us.
            constraint_var = fit.wrapped_constraints[0]['fun'].model.dependent_vars[0]
            objective.data[constraint_var] = None
            fit.execute()

        # No scipy style dicts allowed.
        with self.assertRaises(TypeError):
            fit = SLSQP(MinimizeModel(model, data={}),
                        parameters=[a, b, c],
                        constraints=[
                            {'type': 'eq', 'fun': lambda a, b, c: a - c}
                        ]
            )
Пример #5
0
def test_minimizer_constraint_compatibility():
    """
    Test if #156 has been solved, and test all the other constraint styles.
    """
    x, y, z = variables('x, y, z')
    a, b, c = parameters('a, b, c')
    b.fixed = True

    model = Model({z: a * x**2 - b * y**2 + c})
    # Generate data, z has to be scalar for MinimizeModel to be happy
    xdata = 3  # np.linspace(0, 10)
    ydata = 5  # np.linspace(0, 10)
    zdata = model(a=2, b=3, c=5, x=xdata, y=ydata).z
    data_dict = {x: xdata, y: ydata, z: zdata}

    # Equivalent ways of defining the same constraint
    constraint_model = Model.as_constraint(a - c, model, constraint_type=Eq)
    constraint_model.params = model.params
    constraints = [
        Eq(a, c),
        MinimizeModel(constraint_model, data=data_dict), constraint_model
    ]

    objective = MinimizeModel(model, data=data_dict)
    for constraint in constraints:
        fit = SLSQP(objective, parameters=[a, b, c], constraints=[constraint])
        wrapped_constr = fit.wrapped_constraints[0]['fun'].model
        assert isinstance(wrapped_constr, Model)
        assert wrapped_constr.params == model.params
        assert wrapped_constr.jacobian_model.params == model.params
        assert wrapped_constr.hessian_model.params == model.params
        # Set the data for the dependent var of the constraint to None
        # Normally this is handled by Fit because here we interact with the
        # Minimizer directly, it is up to us.
        constraint_var = fit.wrapped_constraints[0][
            'fun'].model.dependent_vars[0]
        objective.data[constraint_var] = None
        fit.execute()

    # No scipy style dicts allowed.
    with pytest.raises(TypeError):
        fit = SLSQP(MinimizeModel(model, data=data_dict),
                    parameters=[a, b, c],
                    constraints=[{
                        'type': 'eq',
                        'fun': lambda a, b, c: a - c
                    }])
Пример #6
0
def test_neg():
    """
    Test negation of all model types
    """
    x, y_1, y_2 = variables('x, y_1, y_2')
    a, b = parameters('a, b')

    model_dict = {y_2: a * x ** 2, y_1: 2 * x * b}
    model = Model(model_dict)

    model_neg = - model
    for key in model:
        assert model[key] == - model_neg[key]

    # Constraints
    constraint = Model.as_constraint(Eq(a * x, 2), model)

    constraint_neg = - constraint
    # for key in constraint:
    assert constraint[constraint.dependent_vars[0]] == - constraint_neg[constraint_neg.dependent_vars[0]]

    # ODEModel
    odemodel = ODEModel({D(y_1, x): a * x}, initial={a: 1.0})

    odemodel_neg = - odemodel
    for key in odemodel:
        assert odemodel[key] == - odemodel_neg[key]

    # For models with interdependency, negation should only change the
    # dependent components.
    model_dict = {x: y_1**2, y_1: a * y_2 + b}
    model = Model(model_dict)

    model_neg = - model
    for key in model:
        if key in model.dependent_vars:
            assert model[key] == - model_neg[key]
        elif key in model.interdependent_vars:
            assert model[key] == model_neg[key]
        else:
            pytest.fail()
Пример #7
0
    def test_neg(self):
        """
        Test negation of all model types
        """
        x, y_1, y_2 = variables('x, y_1, y_2')
        a, b = parameters('a, b')

        model_dict = {y_2: a * x ** 2, y_1: 2 * x * b}
        model = Model(model_dict)

        model_neg = - model
        for key in model:
            self.assertEqual(model[key], - model_neg[key])

        # Constraints
        constraint = Model.as_constraint(Eq(a * x, 2), model)

        constraint_neg = - constraint
        # for key in constraint:
        self.assertEqual(constraint[constraint.dependent_vars[0]], - constraint_neg[constraint_neg.dependent_vars[0]])

        # ODEModel
        odemodel = ODEModel({D(y_1, x): a * x}, initial={a: 1.0})

        odemodel_neg = - odemodel
        for key in odemodel:
            self.assertEqual(odemodel[key], - odemodel_neg[key])

        # For models with interdependency, negation should only change the
        # dependent components.
        model_dict = {x: y_1**2, y_1: a * y_2 + b}
        model = Model(model_dict)

        model_neg = - model
        for key in model:
            if key in model.dependent_vars:
                self.assertEqual(model[key], - model_neg[key])
            elif key in model.interdependent_vars:
                self.assertEqual(model[key], model_neg[key])
            else:
                raise Exception('There should be no such variable')
Пример #8
0
def test_data_for_constraint():
    """
    Test the signature handling when constraints are at play. Constraints
    should take seperate data, but still kwargs that are not found in either
    the model nor the constraints should raise an error.
    """
    A, mu, sig = parameters('A, mu, sig')
    x, y, Y = variables('x, y, Y')

    model = Model({y: A * Gaussian(x, mu=mu, sig=sig)})
    constraint = Model.as_constraint(Y, model, constraint_type=Eq)

    np.random.seed(2)
    xdata = np.random.normal(1.2, 2, 10)
    ydata, xedges = np.histogram(xdata,
                                 bins=int(np.sqrt(len(xdata))),
                                 density=True)

    # Allowed
    fit = Fit(model, x=xdata, y=ydata, Y=2, constraints=[constraint])
    assert isinstance(fit.objective, LeastSquares)
    assert isinstance(fit.minimizer.constraints[0], MinimizeModel)
    fit = Fit(model, x=xdata, y=ydata)
    assert isinstance(fit.objective, LeastSquares)
    fit = Fit(model, x=xdata, objective=LogLikelihood)
    assert isinstance(fit.objective, LogLikelihood)

    # Not allowed
    with pytest.raises(TypeError):
        fit = Fit(model, x=xdata, y=ydata, Y=2)

    with pytest.raises(TypeError):
        fit = Fit(model, x=xdata, y=ydata, Y=2, Z=3, constraints=[constraint])

    with pytest.raises(TypeError):
        fit = Fit(model, x=xdata, y=ydata, objective=LogLikelihood)
Пример #9
0
    def test_constrained_dependent_on_model(self):
        """
        For a simple Gaussian distribution, we test if Models of various types
        can be used as constraints. Of particular interest are NumericalModels,
        which can be used to fix the integral of the model during the fit to 1,
        as it should be for a probability distribution.
        :return:
        """
        A, mu, sig = parameters('A, mu, sig')
        x, y, Y = variables('x, y, Y')
        i = Idx('i', (0, 1000))
        sig.min = 0.0

        model = Model({y: A * Gaussian(x, mu=mu, sig=sig)})

        # Generate data, 100 samples from a N(1.2, 2) distribution
        np.random.seed(2)
        xdata = np.random.normal(1.2, 2, 1000)
        ydata, xedges = np.histogram(xdata, bins=int(np.sqrt(len(xdata))), density=True)
        xcentres = (xedges[1:] + xedges[:-1]) / 2

        # Unconstrained fit
        fit = Fit(model, x=xcentres, y=ydata)
        unconstr_result = fit.execute()

        # Constraints must be scalar models.
        with self.assertRaises(ModelError):
            Model.as_constraint([A - 1, sig - 1], model, constraint_type=Eq)
        constraint_exact = Model.as_constraint(
            A * sqrt(2 * sympy.pi) * sig - 1, model, constraint_type=Eq
        )
        # Only when explicitly asked, do models behave as constraints.
        self.assertTrue(hasattr(constraint_exact, 'constraint_type'))
        self.assertEqual(constraint_exact.constraint_type, Eq)
        self.assertFalse(hasattr(model, 'constraint_type'))

        # Now lets make some valid constraints and see if they are respected!
        # TODO: These first two should be symbolical integrals over `y` instead,
        # but currently this is not converted into a numpy/scipy function. So instead the first two are not valid constraints.
        constraint_model = Model.as_constraint(A - 1, model, constraint_type=Eq)
        constraint_exact = Eq(A, 1)
        constraint_num = CallableNumericalModel.as_constraint(
            {Y: lambda x, y: simps(y, x) - 1},  # Integrate using simps
            model=model,
            connectivity_mapping={Y: {x, y}},
            constraint_type=Eq
        )

        # Test for all these different types of constraint.
        for constraint in [constraint_model, constraint_exact, constraint_num]:
            if not isinstance(constraint, Eq):
                self.assertEqual(constraint.constraint_type, Eq)

            xcentres = (xedges[1:] + xedges[:-1]) / 2
            fit = Fit(model, x=xcentres, y=ydata, constraints=[constraint])
            # Test if conversion into a constraint was done properly
            fit_constraint = fit.constraints[0]
            self.assertEqual(fit.model.params, fit_constraint.params)
            self.assertEqual(fit_constraint.constraint_type, Eq)

            con_map = fit_constraint.connectivity_mapping
            if isinstance(constraint, CallableNumericalModel):
                self.assertEqual(con_map, {Y: {x, y}, y: {x, mu, sig, A}})
                self.assertEqual(fit_constraint.independent_vars, [x])
                self.assertEqual(fit_constraint.dependent_vars, [Y])
                self.assertEqual(fit_constraint.interdependent_vars, [y])
                self.assertEqual(fit_constraint.params, [A, mu, sig])
            else:
                # ToDo: if these constraints can somehow be written as integrals
                # depending on y and x this if/else should be removed.
                self.assertEqual(con_map,
                                 {fit_constraint.dependent_vars[0]: {A}})
                self.assertEqual(fit_constraint.independent_vars, [])
                self.assertEqual(len(fit_constraint.dependent_vars), 1)
                self.assertEqual(fit_constraint.interdependent_vars, [])
                self.assertEqual(fit_constraint.params, [A, mu, sig])

            # Finally, test if the constraint worked
            fit_result = fit.execute(options={'eps': 1e-15, 'ftol': 1e-10})
            unconstr_value = fit.minimizer.wrapped_constraints[0]['fun'](**unconstr_result.params)
            constr_value = fit.minimizer.wrapped_constraints[0]['fun'](**fit_result.params)
            self.assertAlmostEqual(constr_value[0], 0.0, 10)
        # And if it was very poorly met before
        self.assertNotAlmostEqual(unconstr_value[0], 0.0, 2)
Пример #10
0
def test_constrained_dependent_on_model():
    """
    For a simple Gaussian distribution, we test if Models of various types
    can be used as constraints. Of particular interest are NumericalModels,
    which can be used to fix the integral of the model during the fit to 1,
    as it should be for a probability distribution.
    :return:
    """
    A, mu, sig = parameters('A, mu, sig')
    x, y, Y = variables('x, y, Y')
    i = Idx('i', (0, 1000))
    sig.min = 0.0

    model = GradientModel({y: A * Gaussian(x, mu=mu, sig=sig)})

    # Generate data, 100 samples from a N(1.2, 2) distribution
    np.random.seed(2)
    xdata = np.random.normal(1.2, 2, 1000)
    ydata, xedges = np.histogram(xdata,
                                 bins=int(np.sqrt(len(xdata))),
                                 density=True)
    xcentres = (xedges[1:] + xedges[:-1]) / 2

    # Unconstrained fit
    fit = Fit(model, x=xcentres, y=ydata)
    unconstr_result = fit.execute()

    # Constraints must be scalar models.
    with pytest.raises(ModelError):
        Model.as_constraint([A - 1, sig - 1], model, constraint_type=Eq)

    constraint_exact = Model.as_constraint(A * sqrt(2 * sympy.pi) * sig - 1,
                                           model,
                                           constraint_type=Eq)
    # Only when explicitly asked, do models behave as constraints.
    assert hasattr(constraint_exact, 'constraint_type')
    assert constraint_exact.constraint_type == Eq
    assert not hasattr(model, 'constraint_type')

    # Now lets make some valid constraints and see if they are respected!
    # FIXME These first two should be symbolical integrals over `y` instead,
    # but currently this is not converted into a numpy/scipy function. So
    # instead the first two are not valid constraints.
    constraint_model = Model.as_constraint(A - 1, model, constraint_type=Eq)
    constraint_exact = Eq(A, 1)
    constraint_num = CallableNumericalModel.as_constraint(
        {
            Y: lambda x, y: simps(y, x) - 1
        },  # Integrate using simps
        model=model,
        connectivity_mapping={Y: {x, y}},
        constraint_type=Eq)

    # Test for all these different types of constraint.
    for constraint in [constraint_model, constraint_exact, constraint_num]:
        if not isinstance(constraint, Eq):
            assert constraint.constraint_type == Eq

        xcentres = (xedges[1:] + xedges[:-1]) / 2
        fit = Fit(model, x=xcentres, y=ydata, constraints=[constraint])
        # Test if conversion into a constraint was done properly
        fit_constraint = fit.constraints[0]
        assert fit.model.params == fit_constraint.params
        assert fit_constraint.constraint_type == Eq

        con_map = fit_constraint.connectivity_mapping
        if isinstance(constraint, CallableNumericalModel):
            assert con_map == {Y: {x, y}, y: {x, mu, sig, A}}
            assert fit_constraint.independent_vars == [x]
            assert fit_constraint.dependent_vars == [Y]
            assert fit_constraint.interdependent_vars == [y]
            assert fit_constraint.params == [A, mu, sig]
        else:
            # TODO if these constraints can somehow be written as integrals
            # depending on y and x this if/else should be removed.
            assert con_map == {fit_constraint.dependent_vars[0]: {A}}
            assert fit_constraint.independent_vars == []
            assert len(fit_constraint.dependent_vars) == 1
            assert fit_constraint.interdependent_vars == []
            assert fit_constraint.params == [A, mu, sig]

        # Finally, test if the constraint worked
        fit_result = fit.execute(options={'eps': 1e-15, 'ftol': 1e-10})
        unconstr_value = fit.minimizer.wrapped_constraints[0]['fun'](
            **unconstr_result.params)
        constr_value = fit.minimizer.wrapped_constraints[0]['fun'](
            **fit_result.params)

        # TODO because of a bug by pytest we have to solve it like this
        assert constr_value[0] == pytest.approx(0, abs=1e-10)
    # And if it was very poorly met before
    assert not unconstr_value[0] == pytest.approx(0.0, 1e-1)