def test_vector_fitting(self): """ Test the behavior in the presence of bounds or constraints: `Fit` should select `ConstrainedNumericalLeastSquares` when bounds or constraints are provided, or for vector models in general. For scalar models, use `NumericalLeastSquares`. """ a, b, c = parameters('a, b, c') a_i, b_i, c_i = variables('a_i, b_i, c_i') model = {a_i: a, b_i: b, c_i: c} xdata = np.array([ [10.1, 9., 10.5, 11.2, 9.5, 9.6, 10.], [102.1, 101., 100.4, 100.8, 99.2, 100., 100.8], [71.6, 73.2, 69.5, 70.2, 70.8, 70.6, 70.1], ]) # Make a new scalar model. scalar_model = {a_i: a + b} simple_fit = Fit( model=scalar_model, a_i=xdata[0], ) self.assertIsInstance(simple_fit.fit, NumericalLeastSquares) constrained_fit = Fit(model=scalar_model, a_i=xdata[0], constraints=[Equality(a + b, 110)]) self.assertIsInstance(constrained_fit.fit, ConstrainedNumericalLeastSquares) a.min = 0 a.max = 25 a.value = 10 b.min = 80 b.max = 120 b.value = 100 bound_fit = Fit( model=scalar_model, a_i=xdata[0], ) self.assertIsInstance(bound_fit.fit, ConstrainedNumericalLeastSquares) # Repeat all of the above for the Vector model simple_fit = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], ) self.assertIsInstance(simple_fit.fit, ConstrainedNumericalLeastSquares) constrained_fit = Fit(model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], constraints=[Equality(a + b + c, 180)]) self.assertIsInstance(constrained_fit.fit, ConstrainedNumericalLeastSquares) a.min = 0 a.max = 25 a.value = 10 b.min = 80 b.max = 120 b.value = 100 bound_fit = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], ) self.assertIsInstance(bound_fit.fit, ConstrainedNumericalLeastSquares) fit_result = bound_fit.execute() self.assertAlmostEqual(fit_result.value(a), np.mean(xdata[0]), 6) self.assertAlmostEqual(fit_result.value(b), np.mean(xdata[1]), 6) self.assertAlmostEqual(fit_result.value(c), np.mean(xdata[2]), 6)
def test_vector_fitting(): """ Test the behavior in the presence of bounds or constraints: `Fit` should select `ConstrainedNumericalLeastSquares` when bounds or constraints are provided, or for vector models in general. For scalar models, use `NumericalLeastSquares`. """ a, b = parameters('a, b') a_i, = variables('a_i') xdata = np.array([ [10.1, 9., 10.5, 11.2, 9.5, 9.6, 10.], [102.1, 101., 100.4, 100.8, 99.2, 100., 100.8], [71.6, 73.2, 69.5, 70.2, 70.8, 70.6, 70.1], ]) # Make a new scalar model. scalar_model = {a_i: a + b} simple_fit = Fit(model=scalar_model, a_i=xdata[0], minimizer=MINPACK) assert isinstance(simple_fit.minimizer, MINPACK) constrained_fit = Fit(model=scalar_model, a_i=xdata[0], constraints=[Equality(a + b, 110)]) assert isinstance(constrained_fit.minimizer, SLSQP) a.min = 0 a.max = 25 a.value = 10 b.min = 80 b.max = 120 b.value = 100 bound_fit = Fit( model=scalar_model, a_i=xdata[0], ) assert isinstance(bound_fit.minimizer, LBFGSB) # Repeat all of the above for the Vector model a, b, c = parameters('a, b, c') a_i, b_i, c_i = variables('a_i, b_i, c_i') model = {a_i: a, b_i: b, c_i: c} simple_fit = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], ) assert isinstance(simple_fit.minimizer, BFGS) constrained_fit = Fit(model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], constraints=[Equality(a + b + c, 180)]) assert isinstance(constrained_fit.minimizer, SLSQP) a.min = 0 a.max = 25 a.value = 10 b.min = 80 b.max = 120 b.value = 100 bound_fit = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], ) assert isinstance(bound_fit.minimizer, LBFGSB) fit_result = bound_fit.execute() assert fit_result.value(a) == pytest.approx(np.mean(xdata[0]), rel=1e-6) assert fit_result.value(b) == pytest.approx(np.mean(xdata[1]), rel=1e-6) assert fit_result.value(c) == pytest.approx(np.mean(xdata[2]), rel=1e-6)
def test_vector_constrained_fitting(self): """ Tests `Fit` with vector models. The classical example of fitting measurements of the angles of a triangle is taken. In this case we know they should add up to 180 degrees, so this can be added as a constraint. Additionally, not even all three angles have to be provided with measurement data since the constrained means the angles are not independent. """ a, b, c = parameters('a, b, c') a_i, b_i, c_i = variables('a_i, b_i, c_i') model = {a_i: a, b_i: b, c_i: c} xdata = np.array([ [10.1, 9., 10.5, 11.2, 9.5, 9.6, 10.], [102.1, 101., 100.4, 100.8, 99.2, 100., 100.8], [71.6, 73.2, 69.5, 70.2, 70.8, 70.6, 70.1], ]) fit_none = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=None, ) fit = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], ) fit_std = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], minimizer = MINPACK ) fit_constrained = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=xdata[2], constraints=[Equality(a + b + c, 180)] ) fit_none_result = fit_none.execute() fit_new_result = fit.execute() std_result = fit_std.execute() constr_result = fit_constrained.execute() # The total of averages should equal the total of the params by definition mean_total = np.mean(np.sum(xdata, axis=0)) params_tot = std_result.value(a) + std_result.value(b) + std_result.value(c) self.assertAlmostEqual(mean_total / params_tot, 1.0, 4) # The total after constraining to 180 should be exactly 180. params_tot = constr_result.value(a) + constr_result.value(b) + constr_result.value(c) self.assertIsInstance(fit_constrained.minimizer, SLSQP) self.assertAlmostEqual(180.0, params_tot, 4) # The standard method and the Constrained object called without constraints # should behave roughly the same. self.assertAlmostEqual(fit_new_result.value(a), std_result.value(a), 4) self.assertAlmostEqual(fit_new_result.value(b), std_result.value(b), 4) self.assertAlmostEqual(fit_new_result.value(c), std_result.value(c), 4) # When fitting with a dataset set to None, for this example the value of c # should be unaffected. self.assertAlmostEqual(fit_none_result.value(a), std_result.value(a), 4) self.assertAlmostEqual(fit_none_result.value(b), std_result.value(b), 4) self.assertAlmostEqual(fit_none_result.value(c), c.value) fit_none_constr = Fit( model=model, a_i=xdata[0], b_i=xdata[1], c_i=None, constraints=[Equality(a + b + c, 180)] ) none_constr_result = fit_none_constr.execute() params_tot = none_constr_result.value(a) + none_constr_result.value(b) + none_constr_result.value(c) self.assertAlmostEqual(180.0, params_tot, 4)