def test_interpolation_inversion(): m = odijk("Nucleosome").invert(independent_max=120.0, interpolate=True) parvec = [5.77336105517341, 7.014180463612673, 1500.0000064812095, 4.11] result = np.array( [0.17843862, 0.18101283, 0.18364313, 0.18633117, 0.18907864]) np.testing.assert_allclose( m._raw_call(np.arange(10, 250, 50) / 1000, parvec), result)
def test_fd_variable_order(): # Fit takes dependent, then independent m = odijk("M") fit = Fit(m) fit._add_data("test", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test"].x, [1, 2, 3]) np.testing.assert_allclose(fit[m].data["test"].y, [2, 3, 4]) fit[m]._add_data("test2", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test2"].x, [1, 2, 3]) np.testing.assert_allclose(fit[m].data["test2"].y, [2, 3, 4]) m = inverted_odijk("M") fit = Fit(m) fit._add_data("test", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test"].x, [1, 2, 3]) np.testing.assert_allclose(fit[m].data["test"].y, [2, 3, 4]) fit[m]._add_data("test2", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test2"].x, [1, 2, 3]) np.testing.assert_allclose(fit[m].data["test2"].y, [2, 3, 4]) # FdFit always takes f, d and maps it to the correct values m = odijk("M") fit = FdFit(m) # Test the FdFit interface fit.add_data("test", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test"].x, [1, 2, 3]) np.testing.assert_allclose(fit[m].data["test"].y, [2, 3, 4]) # Test the FdDatasets interface fit[m].add_data("test2", [3, 4, 5], [4, 5, 6]) np.testing.assert_allclose(fit[m].data["test2"].x, [3, 4, 5]) np.testing.assert_allclose(fit[m].data["test2"].y, [4, 5, 6]) m = inverted_odijk("M") fit = FdFit(m) fit.add_data("test", [1, 2, 3], [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test"].x, [2, 3, 4]) np.testing.assert_allclose(fit[m].data["test"].y, [1, 2, 3]) # Test the FdDatasets interface fit[m].add_data("test2", [3, 4, 5], [4, 5, 6]) np.testing.assert_allclose(fit[m].data["test2"].x, [4, 5, 6]) np.testing.assert_allclose(fit[m].data["test2"].y, [3, 4, 5])
def test_custom_legend_labels(): """Test whether users can provide a custom label for plotting""" def test_labels(labels): for legend_entry, label in zip(plt.gca().get_legend().texts, labels): assert label == legend_entry.get_text() fit = Fit(odijk("m")) fit._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit.plot() test_labels(["data_1 (model)", "data_1 (data)"]) plt.gca().clear() fit.plot(label="custom label") test_labels(["custom label (model)", "custom label (data)"])
def test_jacobian_test_fit(): def f(independent, a, b): return a + b * independent def f_jac(independent, a, b): del a, b return np.vstack((np.ones((1, len(independent))), independent)) def f_der(independent, a, b): del a return b * np.ones((len(independent))) def f_jac_wrong(independent, a, b): del a, b return np.vstack((2.0 * np.ones((1, len(independent))), independent)) x = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) a_true, b_true = (5.0, 5.0) f_data = f(x, a_true, b_true) model = Model("f", f, jacobian=f_jac, derivative=f_der) fit = Fit(model) fit._add_data("test", x, f_data) fit.params["f/a"].value = a_true fit.params["f/b"].value = b_true assert fit.verify_jacobian(fit.params.values) model_bad = Model("f", f, jacobian=f_jac_wrong, derivative=f_der) fit = Fit(model_bad) fit._add_data("test", x, f_data) fit.params["f/a"].value = a_true fit.params["f/b"].value = b_true assert not fit.verify_jacobian(fit.params.values) with pytest.raises(ValueError): assert odijk("WLC").verify_jacobian([1.0, 2.0, 3.0], [1.0, 2.0]) with pytest.raises(ValueError): odijk("WLC").verify_derivative([1, 2, 3], [1, 2, 3])
def test_fit_reprs(): m = odijk("DNA") fit = Fit(m) d1 = fit._add_data("data_1", [1, 2, 3], [2, 3, 4]) assert d1.__repr__() == "FitData(data_1, N=3)" d2 = fit._add_data("dataset_2", [1, 2, 3], [2, 3, 4], {"DNA/Lc": "DNA/Lc_2"}) assert d2.__repr__( ) == "FitData(dataset_2, N=3, Transformations: DNA/Lc → DNA/Lc_2)" f = Fit(m) assert f.__repr__() assert f._repr_html_() fit._add_data("data_3", [1, 2, 3], [2, 3, 4], {"DNA/Lc": 5}) assert fit.__repr__() assert fit._repr_html_() m = inverted_odijk("DNA") fit = FdFit(m) fit._add_data("RecA", [1, 2, 3], [1, 2, 3]) fit._add_data("RecA2", [1, 2, 3], [1, 2, 3]) fit._add_data("RecA3", [1, 2, 3], [1, 2, 3]) assert fit[m].__repr__( ) == "lumicks.pylake.FdDatasets(datasets={RecA, RecA2, RecA3}, N=9)" assert ( fit[m].__str__() == "Data sets:\n- FitData(RecA, N=3)\n- FitData(RecA2, N=3)\n- FitData(RecA3, N=3)\n" ) assert fit[m]._repr_html_() == ("  FitData(RecA, N=3)<br>\n" "  FitData(RecA2, N=3)<br>\n" "  FitData(RecA3, N=3)<br>\n") assert fit.__repr__() == "lumicks.pylake.FdFit(models={DNA}, N=9)" assert fit.__str__() == ( "Fit\n - Model: DNA\n - Equation:\n" " f(d) = argmin[f](norm(DNA.Lc * (1 - (1/2)*sqrt(kT/(f*DNA.Lp)) + f/DNA.St)-d))\n\n" " - Data sets:\n - FitData(RecA, N=3)\n - FitData(RecA2, N=3)\n " "- FitData(RecA3, N=3)\n\n " "- Fitted parameters:\n" " Name Value Unit Fitted Lower bound Upper bound\n" " ------ ------- -------- -------- ------------- -------------\n" " DNA/Lp 40 [nm] True 0 100\n" " DNA/Lc 16 [micron] True 0 inf\n" " DNA/St 1500 [pN] True 0 inf\n" " kT 4.11 [pN*nm] False 0 8" )
def test_simulation_api(): dna = odijk("DNA") force = [0.1, 0.2, 0.3] np.testing.assert_allclose( dna(force, {"DNA/Lp": 50.0, "DNA/Lc": 16.0, "DNA/St": 1500.0, "kT": 4.11}), [8.74792941, 10.8733908, 11.81559925], ) np.testing.assert_allclose( dna( [0.1, 0.2, 0.3], Params( **{ "DNA/Lp": Parameter(50.0), "DNA/Lc": Parameter(16.0), "DNA/St": Parameter(1500.0), "kT": Parameter(4.11), } ), ), [8.74792941, 10.8733908, 11.81559925], )
def test_simulation_api_wrong_par(): dna = odijk("DNA") with pytest.raises(KeyError): dna([1], {"DNA/Lp": 50.0, "DNA/Lc": 16.0, "DN/St": 1500.0, "kT": 4.11})
def test_plotting(): m = odijk("DNA") m2 = odijk("protein") # Test single model plotting fit = Fit(m) fit[m]._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit.plot() fit.plot("data_1") with pytest.raises(AssertionError): fit.plot("non-existent-data") fit.plot(overrides={"DNA/Lc": 12}) with pytest.raises(KeyError): fit.plot(overrides={"DNA/c": 12}) fit.plot(overrides={"DNA/Lc": 12}, independent=np.arange(1.0, 5.0, 1.0)) with pytest.raises(KeyError): fit[m2].plot() # Test multi-model plotting fit = Fit(m, m2) fit[m]._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit[m]._add_data("dataset_2", [1, 2, 3], [2, 3, 4], {"DNA/Lc": "DNA/Lc_2"}) fit[m2]._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit[m2]._add_data("dataset_2", [1, 2, 3], [2, 3, 4], {"protein/Lc": "protein/Lc_2"}) fit[m2]._add_data("dataset 3", [1, 2, 3], [2, 3, 4], {"protein/Lc": "protein/Lc_2"}) with pytest.raises(AssertionError): fit.plot() fit[m].plot() fit[m2].plot() fit[m].plot("data_1") with pytest.raises(AssertionError): fit.plot(m, "non-existent-data") fit[m2].plot("dataset 3") with pytest.raises(AssertionError): fit[m].plot("dataset 3") fit[m].plot(overrides={"DNA/Lc": 12}) with pytest.raises(KeyError): fit[m].plot(overrides={"DNA/c": 12}) independent = np.arange(0.15, 2, 0.25) params = [38.18281266, 0.37704827, 278.50103452, 4.11] odijk("WLC").verify_jacobian(independent, params, plot=1) params = [38.18281266, 0.37704827, 278.50103452, 4.11] fit = FdFit(odijk("WLC")) fit.add_data("dataset 3", [1, 2, 3], [2, 3, 4]) plt.figure() fit.verify_jacobian(params, plot=1) # Test live fit plotting fit = Fit(m, m2) fit[m]._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit[m]._add_data("dataset_2", [1, 2, 3], [2, 3, 4], {"DNA/Lc": "DNA/Lc_2"}) fit[m2]._add_data("data_1", [1, 2, 3], [2, 3, 4]) fit.fit(show_fit=True, max_nfev=1)
def test_model_composition(): def f(x, a, b): return a + b * x def f_jac(x, a, b): return np.vstack((np.ones((1, len(x))), x)) def f_jac_wrong(x, a, b): return np.vstack((np.zeros((1, len(x))), x)) def g(x, a, d, b): return a - b * x + d * x * x def g_jac(x, a, d, b): return np.vstack((np.ones((1, len(x))), x * x, -x)) def f_der(x, a, b): return b * np.ones((len(x))) def f_der_wrong(x, a, b): return np.ones((len(x))) def g_der(x, a, d, b): return -b * np.ones((len(x))) + 2.0 * d * x m1 = Model("M", f, dependent="x", jacobian=f_jac, derivative=f_der) m2 = Model("M", g, dependent="x", jacobian=g_jac, derivative=g_der) t = np.arange(0, 2, 0.5) # Check actual composition # (a + b * x) + a - b * x + d * x * x = 2 * a + d * x * x np.testing.assert_allclose( (m1 + m2)._raw_call(t, np.array([1.0, 2.0, 3.0])), 2.0 + 3.0 * t * t ), "Model composition returns invalid function evaluation (parameter order issue?)" # Check correctness of the Jacobians and derivatives assert (m1 + m2).verify_jacobian(t, [1.0, 2.0, 3.0]) assert (m1 + m2).verify_derivative(t, [1.0, 2.0, 3.0]) assert (m2 + m1).verify_jacobian(t, [1.0, 2.0, 3.0]) assert (m2 + m1).verify_derivative(t, [1.0, 2.0, 3.0]) assert (m2 + m1 + m2).verify_jacobian(t, [1.0, 2.0, 3.0]) assert (m2 + m1 + m2).verify_derivative(t, [1.0, 2.0, 3.0]) m1_wrong_jacobian = Model("M", f, dependent="x", jacobian=f_jac_wrong, derivative=f_der) assert not (m1_wrong_jacobian + m2).verify_jacobian(t, [1.0, 2.0, 3.0], verbose=False) assert not (m2 + m1_wrong_jacobian).verify_jacobian(t, [1.0, 2.0, 3.0], verbose=False) assert (InverseModel(m1) + m2).verify_jacobian(t, [-1.0, 2.0, 3.0], verbose=False) assert InverseModel(m1 + m2).verify_jacobian(t, [-1.0, 2.0, 3.0], verbose=False) assert InverseModel(m1 + m2 + m1).verify_jacobian(t, [-1.0, 2.0, 3.0], verbose=False) assert (InverseModel(m1) + m2).verify_derivative(t, [-1.0, 2.0, 3.0]) assert InverseModel(m1 + m2).verify_derivative(t, [-1.0, 2.0, 3.0]) assert InverseModel(m1 + m2 + m1).verify_derivative(t, [-1.0, 2.0, 3.0]) m1_wrong_derivative = Model("M", f, dependent="x", jacobian=f_jac, derivative=f_der_wrong) assert not (InverseModel(m1_wrong_derivative) + m2).verify_jacobian( t, [-1.0, 2.0, 3.0], verbose=False) assert not (InverseModel(m1_wrong_jacobian) + m2).verify_jacobian( t, [-1.0, 2.0, 3.0], verbose=False) assert not (InverseModel(m1_wrong_derivative) + m2).verify_derivative( t, [-1.0, 2.0, 3.0]) assert m1.subtract_independent_offset().verify_jacobian(t, [-1.0, 2.0, 3.0], verbose=False) assert m1.subtract_independent_offset().verify_derivative( t, [-1.0, 2.0, 3.0]) m1 = inverted_odijk("DNA").subtract_independent_offset() + force_offset( "f") m2 = (odijk("DNA") + distance_offset("DNA_d")).invert() + force_offset("f") t = np.array([0.19, 0.2, 0.3]) p1 = np.array([0.1, 4.9e1, 3.8e-1, 2.1e2, 4.11, 1.5]) p2 = np.array([4.9e1, 3.8e-1, 2.1e2, 4.11, 0.1, 1.5]) np.testing.assert_allclose(m1._raw_call(t, p1), m2._raw_call(t, p2)) # Check whether incompatible variables are found with pytest.raises(AssertionError): distance_offset("d") + force_offset("f") composite = distance_offset("d") + odijk("DNA") assert composite.dependent == "d" assert composite.independent == "f" assert composite._dependent_unit == "micron" assert composite._independent_unit == "pN" inverted = composite.invert() assert inverted.dependent == "f" assert inverted.independent == "d" assert inverted._dependent_unit == "pN" assert inverted._independent_unit == "micron"
assert composite.dependent == "d" assert composite.independent == "f" assert composite._dependent_unit == "micron" assert composite._independent_unit == "pN" inverted = composite.invert() assert inverted.dependent == "f" assert inverted.independent == "d" assert inverted._dependent_unit == "pN" assert inverted._independent_unit == "micron" @pytest.mark.parametrize( "model,param,unit", [ (odijk("m").subtract_independent_offset(), "m/f_offset", "pN"), (inverted_odijk("m").subtract_independent_offset(), "m/d_offset", "micron"), ((odijk("m") + odijk("m")).subtract_independent_offset(), "m_with_m/f_offset", "pN"), (odijk("m").invert().subtract_independent_offset(), "inv(m)/d_offset", "micron"), (Model("m", lambda c, a: c + a).subtract_independent_offset(), "m/c_offset", "au"), ( Model("m", lambda c, a: c + a).invert().subtract_independent_offset(), "inv(m)/y_offset", "au", ), ],