diff --git a/news/cvxpy.rst b/news/cvxpy.rst new file mode 100644 index 0000000..ab38e48 --- /dev/null +++ b/news/cvxpy.rst @@ -0,0 +1,25 @@ +**Added:** + +* L-BFGS-B method in scipy for weight optimization in def get_weights +* Support for Python 3.13 + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* cvxpy dependency for linear weight optimization in def get_weights +* Support for Python 3.10 + +**Fixed:** + +* Absolute tolerance for updated weighted matrix from 0.5 to 1e-05 in test_subroutines.py + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index 8bd07d0..69fe065 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ maintainers = [ description = "A python package implementing the stretched NMF algorithm." keywords = ['diffpy', 'PDF'] readme = "README.rst" -requires-python = ">=3.10, <3.13" +requires-python = ">=3.11, <3.14" classifiers = [ 'Development Status :: 4 - Beta', 'Environment :: Console', diff --git a/requirements/conda.txt b/requirements/conda.txt index 411cf1b..7f992d1 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -1,5 +1,4 @@ numpy scipy -cvxpy diffpy.utils numdifftools diff --git a/requirements/pip.txt b/requirements/pip.txt index 411cf1b..7f992d1 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -1,5 +1,4 @@ numpy scipy -cvxpy diffpy.utils numdifftools diff --git a/src/diffpy/snmf/optimizers.py b/src/diffpy/snmf/optimizers.py index 4198907..d1a8e51 100644 --- a/src/diffpy/snmf/optimizers.py +++ b/src/diffpy/snmf/optimizers.py @@ -1,5 +1,5 @@ -import cvxpy import numpy as np +from scipy.optimize import minimize def get_weights(stretched_component_gram_matrix, linear_coefficient, lower_bound, upper_bound): @@ -36,20 +36,19 @@ def get_weights(stretched_component_gram_matrix, linear_coefficient, lower_bound input set. Has length C """ + stretched_component_gram_matrix = np.asarray(stretched_component_gram_matrix) linear_coefficient = np.asarray(linear_coefficient) upper_bound = np.asarray(upper_bound) lower_bound = np.asarray(lower_bound) - problem_size = max(linear_coefficient.shape) - solution_variable = cvxpy.Variable(problem_size) - - objective = cvxpy.Minimize( - linear_coefficient.T @ solution_variable - + 0.5 * cvxpy.quad_form(solution_variable, stretched_component_gram_matrix) - ) - constraints = [lower_bound <= solution_variable, solution_variable <= upper_bound] + # Set dynamic bounds based on the size of the linear coefficient + bounds = [(lower_bound, upper_bound) for _ in range(len(linear_coefficient))] + initial_guess = np.zeros_like(linear_coefficient) - cvxpy.Problem(objective, constraints).solve() + # Find optimal weights of linear coefficients + def obj_func(y): + return linear_coefficient.T @ y + 0.5 * y.T @ stretched_component_gram_matrix @ y - return solution_variable.value + result = minimize(obj_func, initial_guess, method="L-BFGS-B", bounds=bounds) + return result.x diff --git a/src/diffpy/snmf/polynomials.py b/src/diffpy/snmf/polynomials.py index 265be05..df68e47 100644 --- a/src/diffpy/snmf/polynomials.py +++ b/src/diffpy/snmf/polynomials.py @@ -1,7 +1,7 @@ import numpy as np -def rooth(linear_coefficient, constant_term): +def compute_root(linear_coefficient, constant_term): """ Returns the largest real root of x^3+(linear_coefficient) * x + constant_term. If there are no real roots return 0. @@ -19,7 +19,6 @@ def rooth(linear_coefficient, constant_term): The largest real root of x^3+(linear_coefficient) * x + constant_term if roots are real, else return 0 array - """ linear_coefficient = np.asarray(linear_coefficient) constant_term = np.asarray(constant_term) diff --git a/tests/test_containers.py b/tests/test_containers.py index 1c78be0..9595ae5 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -3,41 +3,59 @@ from diffpy.snmf.containers import ComponentSignal -tas = [ - ( - [np.arange(10), 3, 0, [6.55, 0.357, 8.49, 9.33, 6.78, 7.57, 7.43, 3.92, 6.55, 1.71], 0.25], - [ - [6.55, 6.78, 6.55, 0, 0, 0, 0, 0, 0, 0], - [0, 14.07893122, 35.36478086, 0, 0, 0, 0, 0, 0, 0], - [0, -19.92049156, 11.6931482, 0, 0, 0, 0, 0, 0, 0], - ], - ), - ( - [np.arange(5), 10, 0, [-11.47, -10.688, -8.095, -29.44, 14.38], 1.25], - [ - [-11.47, -10.8444, -9.1322, -16.633, -20.6760], - [0, -0.50048, -3.31904, 40.9824, -112.1792], - [0, 0.800768, 5.310464, -65.57184, 179.48672], - ], - ), - ( - [np.arange(5), 2, 0, [-11.47, -10.688, -8.095, -29.44, 14.38], 0.88], - [ - [-11.47, -10.3344, -13.9164, -11.5136, 0], - [0, -3.3484, 55.1265, -169.7572, 0], - [0, 7.609997, -125.2876, 385.81189, 0], - ], - ), - ( - [np.arange(10), 1, 2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0.88], - [ - [1, 2.1364, 3.2727, 4.4091, 5.5455, 6.6818, 7.8182, 8.9545, 0, 0], - [0, -1.29, -2.58, -3.87, -5.165, -6.45, -7.74, -9.039, 0, 0], - [0, 2.93, 5.869, 8.084, 11.739, 14.674, 17.608, 20.5437, 0, 0], - ], - ), - ( - [ + +@pytest.mark.parametrize( + "grid, number_of_signals, id_number, iq, stretching_factor, expected", + [ + ( + np.arange(10), + 3, + 0, + [6.55, 0.357, 8.49, 9.33, 6.78, 7.57, 7.43, 3.92, 6.55, 1.71], + 0.25, + [ + [6.55, 6.78, 6.55, 0, 0, 0, 0, 0, 0, 0], + [0, 14.07893122, 35.36478086, 0, 0, 0, 0, 0, 0, 0], + [0, -19.92049156, 11.6931482, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ( + np.arange(5), + 10, + 0, + [-11.47, -10.688, -8.095, -29.44, 14.38], + 1.25, + [ + [-11.47, -10.8444, -9.1322, -16.633, -20.6760], + [0, -0.50048, -3.31904, 40.9824, -112.1792], + [0, 0.800768, 5.310464, -65.57184, 179.48672], + ], + ), + ( + np.arange(5), + 2, + 0, + [-11.47, -10.688, -8.095, -29.44, 14.38], + 0.88, + [ + [-11.47, -10.3344, -13.9164, -11.5136, 0], + [0, -3.3484, 55.1265, -169.7572, 0], + [0, 7.609997, -125.2876, 385.81189, 0], + ], + ), + ( + np.arange(10), + 1, + 2, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + 0.88, + [ + [1, 2.1364, 3.2727, 4.4091, 5.5455, 6.6818, 7.8182, 8.9545, 0, 0], + [0, -1.29, -2.58, -3.87, -5.165, -6.45, -7.74, -9.039, 0, 0], + [0, 2.93, 5.869, 8.084, 11.739, 14.674, 17.608, 20.5437, 0, 0], + ], + ), + ( np.arange(14), 100, 3, @@ -58,53 +76,56 @@ 2.7960, ], 0.55, - ], - [ - [-2.9384, -1.9769, 0.9121, 0.6314, 0.8622, -2.4239, -0.2302, 1.9281, 0, 0, 0, 0, 0, 0], - [0, 2.07933, 38.632, 18.3748, 43.07305, -61.557, 26.005, -73.637, 0, 0, 0, 0, 0, 0], - [0, -7.56, -140.480, -66.81, -156.6293, 223.84, -94.564, 267.7734, 0, 0, 0, 0, 0, 0], - ], - ), - ( - [np.arange(11), 20, 4, [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5], 0.987], - [ - [0, 0.2533, 0.5066, 0.7599, 1.0132, 1.2665, 1.5198, 1.7730, 2.0263, 2.2796, 0], - [0, -0.2566, -0.5132, -0.7699, -1.0265, -1.2831, -1.5398, -1.7964, -2.0530, -2.3097, 0], - [0, 0.5200, 1.0400, 1.56005, 2.08007, 2.6000, 3.1201, 3.6401, 4.1601, 4.6801, 0], - ], - ), - ( - [np.arange(9), 15, 3, [-1, -2, -3, -4, -5, -6, -7, -8, -9], -0.4], - [[-1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]], - ), -] - - -@pytest.mark.parametrize("tas", tas) -def test_apply_stretch(tas): - component = ComponentSignal(tas[0][0], tas[0][1], tas[0][2]) - component.iq = tas[0][3] - component.stretching_factors[0] = tas[0][4] + [ + [-2.9384, -1.9769, 0.9121, 0.6314, 0.8622, -2.4239, -0.2302, 1.9281, 0, 0, 0, 0, 0, 0], + [0, 2.07933, 38.632, 18.3748, 43.07305, -61.557, 26.005, -73.637, 0, 0, 0, 0, 0, 0], + [0, -7.56, -140.480, -66.81, -156.6293, 223.84, -94.564, 267.7734, 0, 0, 0, 0, 0, 0], + ], + ), + ( + np.arange(11), + 20, + 4, + [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5], + 0.987, + [ + [0, 0.2533, 0.5066, 0.7599, 1.0132, 1.2665, 1.5198, 1.7730, 2.0263, 2.2796, 0], + [0, -0.2566, -0.5132, -0.7699, -1.0265, -1.2831, -1.5398, -1.7964, -2.0530, -2.3097, 0], + [0, 0.5200, 1.0400, 1.56005, 2.08007, 2.6000, 3.1201, 3.6401, 4.1601, 4.6801, 0], + ], + ), + ( + np.arange(9), + 15, + 3, + [-1, -2, -3, -4, -5, -6, -7, -8, -9], + -0.4, + [[-1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]], + ), + ], +) +def test_apply_stretch(grid, number_of_signals, id_number, iq, stretching_factor, expected): + component = ComponentSignal(grid, number_of_signals, id_number) + component.iq = iq + component.stretching_factors[0] = stretching_factor actual = component.apply_stretch(0) - expected = tas[1] np.testing.assert_allclose(actual, expected, rtol=1e-01) -taw = [ - ([np.arange(5), 2, 0, [0, 1, 2, 3, 4], 0.5], [0, 0.5, 1, 1.5, 2]), - ([np.arange(5), 20, 2, [0, -1, -2, -3, -4], 0.25], [0, -0.25, -0.5, -0.75, -1]), - ([np.arange(40), 200, 4, np.arange(0, 10, 0.25), 0.3], np.arange(0, 10, 0.25) * 0.3), - ([np.arange(1), 10, 2, [10.5, 11.5, -10.5], 0], [0, 0, 0]), - ([[-12, -10, -15], 5, 2, [-0.5, -1, -1.2], 0.9], [-0.45, -0.9, -1.08]), - ([[-12, -10, -15], 5, 2, [0, 0, 0], 0.9], [0, 0, 0]), -] - - -@pytest.mark.parametrize("taw", taw) -def test_apply_weight(taw): - component = ComponentSignal(taw[0][0], taw[0][1], taw[0][2]) - component.iq = np.array(taw[0][3]) - component.weights[0] = taw[0][4] +@pytest.mark.parametrize( + "grid, number_of_signals, id_number, iq, weight, expected", + [ + (np.arange(5), 2, 0, [0, 1, 2, 3, 4], 0.5, [0, 0.5, 1, 1.5, 2]), + (np.arange(5), 20, 2, [0, -1, -2, -3, -4], 0.25, [0, -0.25, -0.5, -0.75, -1]), + (np.arange(40), 200, 4, np.arange(0, 10, 0.25), 0.3, np.arange(0, 10, 0.25) * 0.3), + (np.arange(1), 10, 2, [10.5, 11.5, -10.5], 0, [0, 0, 0]), + ([-12, -10, -15], 5, 2, [-0.5, -1, -1.2], 0.9, [-0.45, -0.9, -1.08]), + ([-12, -10, -15], 5, 2, [0, 0, 0], 0.9, [0, 0, 0]), + ], +) +def test_apply_weight(grid, number_of_signals, id_number, iq, weight, expected): + component = ComponentSignal(grid, number_of_signals, id_number) + component.iq = np.array(iq) + component.weights[0] = weight actual = component.apply_weight(0) - expected = taw[1] - np.testing.assert_allclose(actual, expected, rtol=1e-01) + np.testing.assert_allclose(actual, expected, rtol=1e-04) diff --git a/tests/test_factorizers.py b/tests/test_factorizers.py index c20d192..6b8efa0 100644 --- a/tests/test_factorizers.py +++ b/tests/test_factorizers.py @@ -3,19 +3,19 @@ from diffpy.snmf.factorizers import lsqnonneg -tl = [ - ([[[1, 0], [1, 0], [0, 1]], [2, 1, 1]], [1.5, 1.0]), - ([[[2, 3], [1, 2], [0, 0]], [7, 7, 2]], [0, 2.6923]), - ([[[3, 2, 4, 1]], [3.2]], [0, 0, 0.8, 0]), - ([[[-0.4, 0], [0, 0], [-9, -18]], [-2, -3, -4.9]], [0.5532, 0]), - ([[[-0.1, -0.2], [-0.8, -0.9]], [0, 0]], [0, 0]), - ([[[0, 0], [0, 0]], [10, 10]], [0, 0]), - ([[[2], [1], [-4], [-0.3]], [6, 4, 0.33, -5]], 0.767188240872451), -] - -@pytest.mark.parametrize("tl", tl) -def test_lsqnonneg(tl): - actual = lsqnonneg(tl[0][0], tl[0][1]) - expected = tl[1] +@pytest.mark.parametrize( + "stretched_component_matrix, target_signal, expected", + [ + ([[1, 0], [1, 0], [0, 1]], [2, 1, 1], [1.5, 1.0]), + ([[2, 3], [1, 2], [0, 0]], [7, 7, 2], [0, 2.6923]), + ([[3, 2, 4, 1]], [3.2], [0, 0, 0.8, 0]), + ([[-0.4, 0], [0, 0], [-9, -18]], [-2, -3, -4.9], [0.5532, 0]), + ([[-0.1, -0.2], [-0.8, -0.9]], [0, 0], [0, 0]), + ([[0, 0], [0, 0]], [10, 10], [0, 0]), + ([[2], [1], [-4], [-0.3]], [6, 4, 0.33, -5], 0.767188240872451), + ], +) +def test_lsqnonneg(stretched_component_matrix, target_signal, expected): + actual = lsqnonneg(stretched_component_matrix, target_signal) np.testing.assert_array_almost_equal(actual, expected, decimal=4) diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index ab7a71f..3179915 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -2,19 +2,19 @@ from diffpy.snmf.optimizers import get_weights -tm = [ - ([[[1, 0], [0, 1]], [1, 1], [0, 0], [1, 1]], [0, 0]), - ([[[1, 0], [0, 1]], [1, 1], -1, 1], [-1, -1]), - ([[[1.75, 0], [0, 1.5]], [1, 1.2], -1, 1], [-0.571428571428571, -0.8]), - ([[[0.75, 0.2], [0.2, 0.75]], [-0.1, -0.2], -1, 1], [0.066985645933014, 0.248803827751196]), - ([[[2, -1, 0], [-1, 2, -1], [0, -1, 2]], [1, 1, 1], -10, 12], [-1.5, -2, -1.5]), - ([[[2, -1, 0], [-1, 2, -1], [0, -1, 2]], [1, -1, -1], -10, 12], [0, 1, 1]), - ([[[4, 0, 0, 0], [0, 3, 0, 0], [0, 0, 2, 0], [0, 0, 0, 1]], [-2, -3, -4, -1], 0, 1000], [0.5, 1, 2, 1]), -] - -@pytest.mark.parametrize("tm", tm) -def test_get_weights(tm): - expected = tm[1] - actual = get_weights(tm[0][0], tm[0][1], tm[0][2], tm[0][3]) +@pytest.mark.parametrize( + "stretched_component_gram_matrix, linear_coefficient, lower_bound, upper_bound, expected", + [ + ([[1, 0], [0, 1]], [1, 1], 0, 0, [0, 0]), + ([[1, 0], [0, 1]], [1, 1], -1, 1, [-1, -1]), + ([[1.75, 0], [0, 1.5]], [1, 1.2], -1, 1, [-0.571428571428571, -0.8]), + ([[0.75, 0.2], [0.2, 0.75]], [-0.1, -0.2], -1, 1, [0.066985645933014, 0.248803827751196]), + ([[2, -1, 0], [-1, 2, -1], [0, -1, 2]], [1, 1, 1], -10, 12, [-1.5, -2, -1.5]), + ([[2, -1, 0], [-1, 2, -1], [0, -1, 2]], [1, -1, -1], -10, 12, [0, 1, 1]), + ([[4, 0, 0, 0], [0, 3, 0, 0], [0, 0, 2, 0], [0, 0, 0, 1]], [-2, -3, -4, -1], 0, 1000, [0.5, 1, 2, 1]), + ], +) +def test_get_weights(stretched_component_gram_matrix, linear_coefficient, lower_bound, upper_bound, expected): + actual = get_weights(stretched_component_gram_matrix, linear_coefficient, lower_bound, upper_bound) assert actual == pytest.approx(expected, rel=1e-4, abs=1e-6) diff --git a/tests/test_polynomials.py b/tests/test_polynomials.py index d935834..51912f0 100644 --- a/tests/test_polynomials.py +++ b/tests/test_polynomials.py @@ -1,27 +1,27 @@ import numpy as np import pytest -from diffpy.snmf.polynomials import rooth +from diffpy.snmf.polynomials import compute_root -tr = [ - ([0, 0], 0), - ([-99.99, 12.50], 9.936397678254531), - ([-4, 0], 2), - ([10, 0], 0), - ([-7, -7], 3.04891734), - ([100, 72], 0), - ([1, 3], 0), - ([0, -7], 0), - ([-9, 0], 3), - ([-9, 3], 2.8169), - ([[2, 2], 2], [0, 0]), - ([[[2, 2], [2, 2]], 2], [[0, 0], [0, 0]]), - ([[[[3, 2], [-2, -2], [100, 0]]], 2], [[[0, 0], [0, 0], [0, 0]]]), -] - -@pytest.mark.parametrize("tr", tr) -def test_rooth(tr): - actual = rooth(tr[0][0], tr[0][1]) - expected = tr[1] +@pytest.mark.parametrize( + "linear_coefficient, constant, expected", + [ + (0, 0, 0), + (-99.99, 12.50, 9.936397678254531), + (-4, 0, 2), + (10, 0, 0), + (-7, -7, 3.04891734), + (100, 72, 0), + (1, 3, 0), + (0, -7, 0), + (-9, 0, 3), + (-9, 3, 2.8169), + ([2, 2], 2, [0, 0]), + ([[2, 2], [2, 2]], 2, [[0, 0], [0, 0]]), + ([[[3, 2], [-2, -2], [100, 0]]], 2, [[[0, 0], [0, 0], [0, 0]]]), + ], +) +def test_rooth(linear_coefficient, constant, expected): + actual = compute_root(linear_coefficient, constant) np.testing.assert_allclose(actual, expected, rtol=1e-4) diff --git a/tests/test_subroutines.py b/tests/test_subroutines.py index 66029d2..3b3e392 100644 --- a/tests/test_subroutines.py +++ b/tests/test_subroutines.py @@ -17,55 +17,68 @@ update_weights_matrix, ) -to = [ - ([[[1, 2], [3, 4]], [[5, 6], [7, 8]], 1e11, [[1, 2], [3, 4]], [[1, 2], [3, 4]], 1], 2.574e14), - ([[[11, 2], [31, 4]], [[5, 63], [7, 18]], 0.001, [[21, 2], [3, 4]], [[11, 22], [3, 40]], 1], 650.4576), - ([[[1, 2], [3, 4]], [[5, 6], [7, 8]], 1e11, [[1, 2], [3, 4]], [[1, 2], [3, 4]], 0], 2.574e14), -] - -@pytest.mark.parametrize("to", to) -def test_objective_function(to): - actual = objective_function(to[0][0], to[0][1], to[0][2], to[0][3], to[0][4], to[0][5]) - expected = to[1] +@pytest.mark.parametrize( + "residual_matrix, stretching_factor_matrix, smoothness, smoothness_term, component_matrix, sparsity, expected", + [ + ([[1, 2], [3, 4]], [[5, 6], [7, 8]], 1e11, [[1, 2], [3, 4]], [[1, 2], [3, 4]], 1, 2.574e14), + ([[11, 2], [31, 4]], [[5, 63], [7, 18]], 0.001, [[21, 2], [3, 4]], [[11, 22], [3, 40]], 1, 650.4576), + ([[1, 2], [3, 4]], [[5, 6], [7, 8]], 1e11, [[1, 2], [3, 4]], [[1, 2], [3, 4]], 0, 2.574e14), + ], +) +def test_objective_function( + residual_matrix, stretching_factor_matrix, smoothness, smoothness_term, component_matrix, sparsity, expected +): + actual = objective_function( + residual_matrix, stretching_factor_matrix, smoothness, smoothness_term, component_matrix, sparsity + ) assert actual == pytest.approx(expected) -tgso = [ - ( - [0.25, [6.55, 0.357, 8.49, 9.33, 6.78, 7.57, 7.43, 3.92, 6.55, 1.71], 10], +@pytest.mark.parametrize( + "stretching_factor, component, signal_length, expected", + [ ( - [6.55, 6.78, 6.55, 0, 0, 0, 0, 0, 0, 0], - [0, 14.07893122, 35.36478086, 0, 0, 0, 0, 0, 0, 0], - [0, -19.92049156, 11.6931482, 0, 0, 0, 0, 0, 0, 0], + 0.25, + [6.55, 0.357, 8.49, 9.33, 6.78, 7.57, 7.43, 3.92, 6.55, 1.71], + 10, + ( + [6.55, 6.78, 6.55, 0, 0, 0, 0, 0, 0, 0], + [0, 14.07893122, 35.36478086, 0, 0, 0, 0, 0, 0, 0], + [0, -19.92049156, 11.6931482, 0, 0, 0, 0, 0, 0, 0], + ), ), - ), - ( - [1.25, [-11.47, -10.688, -8.095, -29.44, 14.38], 5], ( - [-11.47, -10.8444, -9.1322, -16.633, -20.6760], - [0, -0.50048, -3.31904, 40.9824, -112.1792], - [0, 0.800768, 5.310464, -65.57184, 179.48672], + 1.25, + [-11.47, -10.688, -8.095, -29.44, 14.38], + 5, + ( + [-11.47, -10.8444, -9.1322, -16.633, -20.6760], + [0, -0.50048, -3.31904, 40.9824, -112.1792], + [0, 0.800768, 5.310464, -65.57184, 179.48672], + ), ), - ), - ( - [0.88, [-11.47, -10.688, -8.095, -29.44, 14.38], 5], ( - [-11.47, -10.3344, -13.9164, -11.5136, 0], - [0, -3.3484, 55.1265, -169.7572, 0], - [0, 7.609997, -125.2876, 385.81189, 0], + 0.88, + [-11.47, -10.688, -8.095, -29.44, 14.38], + 5, + ( + [-11.47, -10.3344, -13.9164, -11.5136, 0], + [0, -3.3484, 55.1265, -169.7572, 0], + [0, 7.609997, -125.2876, 385.81189, 0], + ), ), - ), - ( - [0.88, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10], ( - [1, 2.1364, 3.2727, 4.4091, 5.5455, 6.6818, 7.8182, 8.9545, 0, 0], - [0, -1.29, -2.58, -3.87, -5.165, -6.45, -7.74, -9.039, 0, 0], - [0, 2.93, 5.869, 8.084, 11.739, 14.674, 17.608, 20.5437, 0, 0], + 0.88, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + 10, + ( + [1, 2.1364, 3.2727, 4.4091, 5.5455, 6.6818, 7.8182, 8.9545, 0, 0], + [0, -1.29, -2.58, -3.87, -5.165, -6.45, -7.74, -9.039, 0, 0], + [0, 2.93, 5.869, 8.084, 11.739, 14.674, 17.608, 20.5437, 0, 0], + ), ), - ), - ( - [ + ( 0.55, [ -2.9384, @@ -84,38 +97,40 @@ def test_objective_function(to): 2.7960, ], 14, - ], - ( - [-2.9384, -1.9769, 0.9121, 0.6314, 0.8622, -2.4239, -0.2302, 1.9281, 0, 0, 0, 0, 0, 0], - [0, 2.07933, 38.632, 18.3748, 43.07305, -61.557, 26.005, -73.637, 0, 0, 0, 0, 0, 0], - [0, -7.56, -140.480, -66.81, -156.6293, 223.84, -94.564, 267.7734, 0, 0, 0, 0, 0, 0], - ), - ), - ( - [0.987, [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5], 11], - ( - [0, 0.2533, 0.5066, 0.7599, 1.0132, 1.2665, 1.5198, 1.7730, 2.0263, 2.2796, 0], - [0, -0.2566, -0.5132, -0.7699, -1.0265, -1.2831, -1.5398, -1.7964, -2.0530, -2.3097, 0], - [0, 0.5200, 1.0400, 1.56005, 2.08007, 2.6000, 3.1201, 3.6401, 4.1601, 4.6801, 0], - ), - ), - ( - [-0.4, [-1, -2, -3, -4, -5, -6, -7, -8, -9], 9], - ([-1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]), - ), -] - - -@pytest.mark.parametrize("tgso", tgso) -def test_get_stretched_component(tgso): - actual = get_stretched_component(tgso[0][0], tgso[0][1], tgso[0][2]) - expected = tgso[1] + ( + [-2.9384, -1.9769, 0.9121, 0.6314, 0.8622, -2.4239, -0.2302, 1.9281, 0, 0, 0, 0, 0, 0], + [0, 2.07933, 38.632, 18.3748, 43.07305, -61.557, 26.005, -73.637, 0, 0, 0, 0, 0, 0], + [0, -7.56, -140.480, -66.81, -156.6293, 223.84, -94.564, 267.7734, 0, 0, 0, 0, 0, 0], + ), + ), + ( + 0.987, + [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5], + 11, + ( + [0, 0.2533, 0.5066, 0.7599, 1.0132, 1.2665, 1.5198, 1.7730, 2.0263, 2.2796, 0], + [0, -0.2566, -0.5132, -0.7699, -1.0265, -1.2831, -1.5398, -1.7964, -2.0530, -2.3097, 0], + [0, 0.5200, 1.0400, 1.56005, 2.08007, 2.6000, 3.1201, 3.6401, 4.1601, 4.6801, 0], + ), + ), + ( + -0.4, + [-1, -2, -3, -4, -5, -6, -7, -8, -9], + 9, + ([-1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]), + ), + ], +) +def test_get_stretched_component(stretching_factor, component, signal_length, expected): + actual = get_stretched_component(stretching_factor, component, signal_length) np.testing.assert_allclose(actual, expected, rtol=1e-01) -tuwm = [ - ( - [ +@pytest.mark.parametrize( + "component_amount, signal_length, " + "stretching_factor_matrix, component_matrix, data, moment, weights_matrix, method, expected", + [ + ( 2, 2, [[0.5, 0.6], [0.7, 0.8]], @@ -124,15 +139,20 @@ def test_get_stretched_component(tgso): 2, [[0.78, 0.12], [0.5, 0.5]], None, - ], - [[0, 1], [1, 1]], - ), - ( - [2, 3, [[0.5], [0.5]], [[1, 2.5], [1.5, 3], [2, 3.5]], [[1, 2], [3, 4], [5, 6]], 1, [[0.5], [0.5]], None], - [[1], [0.1892]], - ), - ( - [ + [[0.533333, 0.933333], [0.533333, 0.933333]], + ), + ( + 2, + 3, + [[0.5], [0.5]], + [[1, 2.5], [1.5, 3], [2, 3.5]], + [[1, 2], [3, 4], [5, 6]], + 1, + [[0.5], [0.5]], + None, + [[1], [0.1892]], + ), + ( 2, 3, [[0.5, 0.6, 0.7], [0.5, 0.6, 0.7]], @@ -141,11 +161,9 @@ def test_get_stretched_component(tgso): 3, [[0.5, 0.45, 0.4], [0.5, 0.45, 0.4]], None, - ], - [[1, 1, 1], [0.1892, 0.5600, 0.938]], - ), - ( - [ + [[1, 1, 1], [0.1892, 0.5600, 0.938]], + ), + ( 3, 3, [[0.7, 0.8, 0.9], [0.71, 0.72, 0.73], [0.8, 0.85, 0.9]], @@ -154,13 +172,11 @@ def test_get_stretched_component(tgso): 3, [[0.9, 0.4, 0.5], [1, 0, 0.4], [0, 0, 0.98]], None, - ], - [[1, 0.0651, 0], [0.5848, 0.0381, 0.1857], [0, 1, 1]], - ), - ([2, 2, [[0.5], [0.5]], [[0, 0], [0, 0]], [[0, 0], [0, 0]], 1, [[0.6], [0.4]], "align"], [[0], [0]]), - ([1, 3, [[0.5, 0.3]], [[1], [1.1], [1.3]], [[1, 2], [2, 3], [3, 2]], 2, [[0.6, 0.4]], None], [[1, 1]]), - ( - [ + [[1.0, 0.0900485, 0.0], [0.585632, 0.497497, 0.179719], [0.0, 0.52223655, 1.0]], + ), + (2, 2, [[0.5], [0.5]], [[0, 0], [0, 0]], [[0, 0], [0, 0]], 1, [[0.6], [0.4]], "align", [[0], [0]]), + (1, 3, [[0.5, 0.3]], [[1], [1.1], [1.3]], [[1, 2], [2, 3], [3, 2]], 2, [[0.6, 0.4]], None, [[1, 1]]), + ( 2, 2, [[0.5, 0.6], [0.7, 0.8]], @@ -169,11 +185,9 @@ def test_get_stretched_component(tgso): 2, [[0.78, 0.12], [0.5, 0.5]], "align", - ], - [[0, 0], [1.0466, 1.46]], - ), - ( - [ + [[0, 0], [0.8, 1.4]], + ), + ( 2, 3, [[0.5], [0.5]], @@ -182,11 +196,9 @@ def test_get_stretched_component(tgso): 1, [[0.5], [0.5]], "align", - ], - [[1.4], [0]], - ), - ( - [ + [[1.4], [0]], + ), + ( 3, 3, [[0.7, 0.8, 0.9], [0.71, 0.72, 0.73], [0.8, 0.85, 0.9]], @@ -195,28 +207,63 @@ def test_get_stretched_component(tgso): 3, [[0.9, 0.4, 0.5], [1, 0, 0.4], [0, 0, 0.98]], "align", - ], - [[1.2605, 0.0552, 0], [0.2723, 0, 0], [0, 1.0538, 1.1696]], - ), - ([2, 2, [[0.5], [0.5]], [[0, 0], [0, 0]], [[0, 0], [0, 0]], 1, [[0.6], [0.4]], "align"], [[0], [0]]), - ([1, 3, [[0.5, 0.3]], [[1], [1.1], [1.3]], [[1, 2], [2, 3], [3, 2]], 2, [[0.6, 0.4]], "align"], [[1.3383, 2]]), -] - - -@pytest.mark.parametrize("tuwm", tuwm) -def test_update_weights_matrix(tuwm): + [[1.281265, 0.104355, 0], [0.0, 0.0, 0.0], [0.239578, 0.965215, 1.162571]], + ), + (2, 2, [[0.5], [0.5]], [[0, 0], [0, 0]], [[0, 0], [0, 0]], 1, [[0.6], [0.4]], "align", [[0], [0]]), + ( + 1, + 3, + [[0.5, 0.3]], + [[1], [1.1], [1.3]], + [[1, 2], [2, 3], [3, 2]], + 2, + [[0.6, 0.4]], + "align", + [[1.3383, 2]], + ), + ], +) +def test_update_weights_matrix( + component_amount, + signal_length, + stretching_factor_matrix, + component_matrix, + data, + moment, + weights_matrix, + method, + expected, +): actual = update_weights_matrix( - tuwm[0][0], tuwm[0][1], tuwm[0][2], tuwm[0][3], tuwm[0][4], tuwm[0][5], tuwm[0][6], tuwm[0][7] + component_amount, + signal_length, + stretching_factor_matrix, + component_matrix, + data, + moment, + weights_matrix, + method, ) - expected = tuwm[1] - np.testing.assert_allclose(actual, expected, rtol=1e-03, atol=0.5) + + np.testing.assert_allclose(actual, expected, rtol=1e-03, atol=1e-05) -tgrm = [ - ([[[1, 2], [3, 4]], [[0.25], [0.75]], [[0.9], [0.7]], [[11, 22], [33, 44]], 1, 2, 2], [[-9, -22], [-33, -44]]), - ([[[1, 2], [3, 4]], [[1], [1]], [[1], [1]], [[11, 22], [33, 44]], 1, 2, 2], [[-8, -22], [-26, -44]]), - ( - [ +@pytest.mark.parametrize( + "component_matrix, weights_matrix, stretching_matrix, data_input, moment_amount, " + "component_amount, signal_length, expected", + [ + ( + [[1, 2], [3, 4]], + [[0.25], [0.75]], + [[0.9], [0.7]], + [[11, 22], [33, 44]], + 1, + 2, + 2, + [[-9, -22], [-33, -44]], + ), + ([[1, 2], [3, 4]], [[1], [1]], [[1], [1]], [[11, 22], [33, 44]], 1, 2, 2, [[-8, -22], [-26, -44]]), + ( [[1.1, 4.4], [1.2, 4.5], [14, 7.8]], [[0.4, 0.6], [0.75, 0.25]], [[0.9, 0.89], [0.98, 0.88]], @@ -224,12 +271,10 @@ def test_update_weights_matrix(tuwm): 2, 2, 3, - ], - [[-6.26, -18.24], [14.9744, 23.5067], [-0.6, -0.9]], - ), - # positive float - ( - [ + [[-6.26, -18.24], [14.9744, 23.5067], [-0.6, -0.9]], + ), + # positive float + ( [[-1.1, -4.4], [-1.2, -4.5], [-14, -7.8]], [[0.4, 0.6], [0.75, 0.25]], [[0.9, 0.89], [0.98, 0.88]], @@ -237,12 +282,10 @@ def test_update_weights_matrix(tuwm): 2, 2, 3, - ], - [[-13.74, -21.76], [6.0256, 17.6933], [-0.6, -0.9]], - ), - # negative floats - ( - [ + [[-13.74, -21.76], [6.0256, 17.6933], [-0.6, -0.9]], + ), + # negative floats + ( [[0, 0, 0, 0], [0, 0, 0, 0]], [[0.4], [0.2], [0.3], [0.3]], [[0.9], [0.9], [0.9], [0.9]], @@ -250,290 +293,301 @@ def test_update_weights_matrix(tuwm): 1, 4, 2, - ], - [[0, 0, 0, 0], [0, 0, 0, 0]], - ), -] - - -@pytest.mark.parametrize("tgrm", tgrm) -def test_get_residual_matrix(tgrm): + [[0, 0, 0, 0], [0, 0, 0, 0]], + ), + ], +) +def test_get_residual_matrix( + component_matrix, + weights_matrix, + stretching_matrix, + data_input, + moment_amount, + component_amount, + signal_length, + expected, +): actual = get_residual_matrix( - tgrm[0][0], tgrm[0][1], tgrm[0][2], tgrm[0][3], tgrm[0][4], tgrm[0][5], tgrm[0][6] + component_matrix, + weights_matrix, + stretching_matrix, + data_input, + moment_amount, + component_amount, + signal_length, ) - expected = tgrm[1] np.testing.assert_allclose(actual, expected, rtol=1e-04) -trd = [ - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ] - ), - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0)]), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 3), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 4), - ] - ), - # ([]) # Exception expected -] - - -@pytest.mark.parametrize("trd", trd) -def test_reconstruct_data(trd): - actual = reconstruct_data(trd) - assert actual.shape == (len(trd[0].iq), len(trd[0].weights)) - print(actual) - - -tld = [ - (([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], 1]), ([[4, 2, 4], [3, 3, 3], [5, 13, 0]])), - (([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], 0]), ([[1, -1, 1], [0, 0, 0], [2, 10, -3]])), - (([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], 0.5]), ([[2.5, 0.5, 2.5], [1.5, 1.5, 1.5], [3.5, 11.5, -1.5]])), - (([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], -1]), ([[4, 2, 4], [3, 3, 3], [5, 13, 0]])), - (([[[0, 0, 0], [0, 0, 0], [0, 0, 0]], 100]), ([[0, 0, 0], [0, 0, 0], [0, 0, 0]])), - (([[[1.5, 2], [10.5, 1], [0.5, 2]], 1]), ([[2, 2.5], [11, 1.5], [1, 2.5]])), - (([[[-10, -10.5], [-12.2, -12.2], [0, 0]], 1]), ([[2.2, 1.7], [0, 0], [12.2, 12.2]])), -] - - -@pytest.mark.parametrize("tld", tld) -def test_lift_data(tld): - actual = lift_data(tld[0][0], tld[0][1]) - expected = tld[1] +@pytest.mark.parametrize( + "components", + [ + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ] + ), + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0)]), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 3), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 4), + ] + ), + # ([]) # Exception expected + ], +) +def test_reconstruct_data(components): + actual = reconstruct_data(components) + assert actual.shape == (len(components[0].iq), len(components[0].weights)) + + +@pytest.mark.parametrize( + "data_input, lift, expected", + [ + # Correct structure: Each test case should be a tuple with three elements.pt + ([[1, -1, 1], [0, 0, 0], [2, 10, -3]], 1, [[4, 2, 4], [3, 3, 3], [5, 13, 0]]), + ([[1, -1, 1], [0, 0, 0], [2, 10, -3]], 0, [[1, -1, 1], [0, 0, 0], [2, 10, -3]]), + ([[1, -1, 1], [0, 0, 0], [2, 10, -3]], 0.5, [[2.5, 0.5, 2.5], [1.5, 1.5, 1.5], [3.5, 11.5, -1.5]]), + ([[1, -1, 1], [0, 0, 0], [2, 10, -3]], -1, [[4, 2, 4], [3, 3, 3], [5, 13, 0]]), + ([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 100, [[0, 0, 0], [0, 0, 0], [0, 0, 0]]), + ([[1.5, 2], [10.5, 1], [0.5, 2]], 1, [[2, 2.5], [11, 1.5], [1, 2.5]]), + ([[-10, -10.5], [-12.2, -12.2], [0, 0]], 1, [[2.2, 1.7], [0, 0], [12.2, 12.2]]), + ], +) +def test_lift_data(data_input, lift, expected): + actual = lift_data(data_input, lift) np.testing.assert_allclose(actual, expected) -tcc = [ - (2, 3, [0, 0.5, 1, 1.5]), # Regular usage - # (0, 3,[0, .5, 1, 1.5]), # Zero components raise an exception. Not tested -] - - -@pytest.mark.parametrize("tcc", tcc) -def test_initialize_components(tcc): - actual = initialize_components(tcc[0], tcc[1], tcc[2]) - assert len(actual) == tcc[0] - assert len(actual[0].weights) == tcc[1] - assert (actual[0].grid == np.array(tcc[2])).all() - - -tcso = [ - ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0)], 1, 20), - ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0)], 4, 20), - # ([ComponentSignal([0,.5,1,1.5],20,0)],0,20), # Raises an exception - # ([ComponentSignal([0,.5,1,1.5],20,0)],-2,20), # Raises an exception - # ([ComponentSignal([0,.5,1,1.5],20,0)],1,0), # Raises an Exception - # ([ComponentSignal([0,.5,1,1.5],20,0)],1,-3), # Raises an exception - ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 1.5], 20, 1)], 2, 20), - ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 21.5], 20, 1)], 2, 20), - ([ComponentSignal([0, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 21.5], 20, 1)], 2, 20), - # ([ComponentSignal([0,.5,1,1.5],20,0),ComponentSignal([0,.5,1,1.5],20,1)],1,-3), - # Negative signal length. Raises an exception - # ([],1,20), # Empty components. Raises an Exception - # ([],-1,20), # Empty components with negative number of components. Raises an exception - # ([],0,20), # Empty components with zero number of components. Raises an exception - # ([],1,0), # Empty components with zero signal length. Raises an exception. - # ([],-1,-2), # Empty components with negative number of components and signal length Raises an exception. -] - - -@pytest.mark.parametrize("tcso", tcso) -def test_construct_stretching_matrix(tcso): - actual = construct_stretching_matrix(tcso[0], tcso[1], tcso[2]) - for component in tcso[0]: +@pytest.mark.parametrize( + "number_of_components, number_of_signals, grid_vector", + [ + (2, 3, [0, 0.5, 1, 1.5]), # Regular usage + # (0, 3,[0, .5, 1, 1.5]), # Zero components raise an exception. Not tested + ], +) +def test_initialize_componentstest_initialize_components(number_of_components, number_of_signals, grid_vector): + actual = initialize_components(number_of_components, number_of_signals, grid_vector) + assert len(actual) == number_of_components + assert len(actual[0].weights) == number_of_signals + assert (actual[0].grid == np.array(grid_vector)).all() + + +@pytest.mark.parametrize( + "components, number_of_components, number_of_signals", + [ + ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0)], 1, 20), + ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0)], 4, 20), + # ([ComponentSignal([0,.5,1,1.5],20,0)],0,20), # Raises an exception + # ([ComponentSignal([0,.5,1,1.5],20,0)],-2,20), # Raises an exception + # ([ComponentSignal([0,.5,1,1.5],20,0)],1,0), # Raises an Exception + # ([ComponentSignal([0,.5,1,1.5],20,0)],1,-3), # Raises an exception + ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 1.5], 20, 1)], 2, 20), + ([ComponentSignal([0, 0.5, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 21.5], 20, 1)], 2, 20), + ([ComponentSignal([0, 1, 1.5], 20, 0), ComponentSignal([0, 0.5, 1, 21.5], 20, 1)], 2, 20), + # ([ComponentSignal([0,.5,1,1.5],20,0),ComponentSignal([0,.5,1,1.5],20,1)],1,-3), + # Negative signal length. Raises an exception + # ([],1,20), # Empty components. Raises an Exception + # ([],-1,20), # Empty components with negative number of components. Raises an exception + # ([],0,20), # Empty components with zero number of components. Raises an exception + # ([],1,0), # Empty components with zero signal length. Raises an exception. + # ([],-1,-2), # Empty components with negative number of components and signal length Raises an exception. + ], +) +def test_construct_stretching_matrix(components, number_of_components, number_of_signals): + actual = construct_stretching_matrix(components, number_of_components, number_of_signals) + for component in components: np.testing.assert_allclose(actual[component.id, :], component.stretching_factors) # assert actual[component.id, :] == component.stretching_factors -tccm = [ - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0)]), - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 0, 0)]), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 2.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ([ComponentSignal([0.25], 20, 0), ComponentSignal([0.25], 20, 1), ComponentSignal([0.25], 20, 2)]), - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1)]), - # ([ComponentSignal([[0, .25, .5, .75, 1],[0, .25, .5, .75, 1]], 20, 0), - # ComponentSignal([[0, .25, .5, .75, 1],[0, .25, .5, .75, 1]], 20, 1)]), - # iq is multidimensional. Expected to fail - # (ComponentSignal([], 20, 0)), # Expected to fail - # ([]), #Expected to fail -] - - -@pytest.mark.parametrize("tccm", tccm) -def test_construct_component_matrix(tccm): - actual = construct_component_matrix(tccm) - for component in tccm: +@pytest.mark.parametrize( + "components", + [ + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0)]), + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 0, 0)]), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 2.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ([ComponentSignal([0.25], 20, 0), ComponentSignal([0.25], 20, 1), ComponentSignal([0.25], 20, 2)]), + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1)]), + # ([ComponentSignal([[0, .25, .5, .75, 1],[0, .25, .5, .75, 1]], 20, 0), + # ComponentSignal([[0, .25, .5, .75, 1],[0, .25, .5, .75, 1]], 20, 1)]), + # iq is multidimensional. Expected to fail + # (ComponentSignal([], 20, 0)), # Expected to fail + # ([]), #Expected to fail + ], +) +def test_construct_component_matrix(components): + actual = construct_component_matrix(components) + for component in components: np.testing.assert_allclose(actual[component.id], component.iq) -tcwm = [ - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0)]), - # ([ComponentSignal([0,.25,.5,.75,1],0,0)]), # 0 signal length. Failure expected - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), - ComponentSignal([0, 0.25, 0.5, 2.75, 1], 20, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), - ] - ), - ([ComponentSignal([0.25], 20, 0), ComponentSignal([0.25], 20, 1), ComponentSignal([0.25], 20, 2)]), - ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1)]), - # (ComponentSignal([], 20, 0)), # Expected to fail - # ([]), #Expected to fail -] - - -@pytest.mark.parametrize("tcwm", tcwm) -def test_construct_weight_matrix(tcwm): - actual = construct_weight_matrix(tcwm) - for component in tcwm: +@pytest.mark.parametrize( + "components", + [ + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0)]), + # ([ComponentSignal([0,.25,.5,.75,1],0,0)]), # 0 signal length. Failure expected + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), + ComponentSignal([0, 0.25, 0.5, 2.75, 1], 20, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 2), + ] + ), + ([ComponentSignal([0.25], 20, 0), ComponentSignal([0.25], 20, 1), ComponentSignal([0.25], 20, 2)]), + ([ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 0), ComponentSignal([0, 0.25, 0.5, 0.75, 1], 20, 1)]), + # (ComponentSignal([], 20, 0)), # Expected to fail + # ([]), #Expected to fail + ], +) +def test_construct_weight_matrix(components): + actual = construct_weight_matrix(components) + for component in components: np.testing.assert_allclose(actual[component.id], component.weights) -tuw = [ - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], - None, - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], - "align", - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], - None, - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], - "align", - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[-0.5, 1], [1.2, -1.3], [1.1, -1], [0, -1.5], [0, 0.1]], - None, - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - [[-0.5, 1], [1.2, -1.3], [1.1, -1], [0, -1.5], [0, 0.1]], - "align", - ), - # ([ComponentSignal([0, .25, .5, .75, 1], 0, 0), ComponentSignal([0, .25, .5, .75, 1], 0, 1), - # ComponentSignal([0, .25, .5, .75, 1], 0, 2)], [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], None), - # ([ComponentSignal([0, .25, .5, .75, 1], 0, 0), ComponentSignal([0, .25, .5, .75, 1], 0, 1), - # ComponentSignal([0, .25, .5, .75, 1], 0, 2)], [], None), - # ([ComponentSignal([0, .25, .5, .75, 1], 2, 0), ComponentSignal([0, .25, .5, .75, 1], 2, 1), - # ComponentSignal([0, .25, .5, .75, 1], 2, 2)], [], 170), -] - - -@pytest.mark.parametrize("tuw", tuw) -def test_update_weights(tuw): - actual = update_weights(tuw[0], tuw[1], tuw[2]) - assert np.shape(actual) == (len(tuw[0]), len(tuw[0][0].weights)) - - -trs = [ - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - 1, - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), - ], - 0, - ), - ( - [ - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 0), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 1), - ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 2), - ], - 2, - ), - # ([ComponentSignal([0, .25, .5, .75, 1], 2, 0), ComponentSignal([0, .25, .5, .75, 1], 2, 1), - # ComponentSignal([0, .25, .5, .75, 1], 2, 2)], -1), -] +@pytest.mark.parametrize( + "components, data_input, method", + [ + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], + None, + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], + "align", + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], + None, + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], + "align", + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[-0.5, 1], [1.2, -1.3], [1.1, -1], [0, -1.5], [0, 0.1]], + None, + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + [[-0.5, 1], [1.2, -1.3], [1.1, -1], [0, -1.5], [0, 0.1]], + "align", + ), + # ([ComponentSignal([0, .25, .5, .75, 1], 0, 0), ComponentSignal([0, .25, .5, .75, 1], 0, 1), + # ComponentSignal([0, .25, .5, .75, 1], 0, 2)], [[1, 1], [1.2, 1.3], [1.3, 1.4], [1.4, 1.5], [2, 2.1]], + # None), + # ([ComponentSignal([0, .25, .5, .75, 1], 0, 0), ComponentSignal([0, .25, .5, .75, 1], 0, 1), + # ComponentSignal([0, .25, .5, .75, 1], 0, 2)], [], None), + # ([ComponentSignal([0, .25, .5, .75, 1], 2, 0), ComponentSignal([0, .25, .5, .75, 1], 2, 1), + # ComponentSignal([0, .25, .5, .75, 1], 2, 2)], [], 170), + ], +) +def test_update_weights(components, data_input, method): + actual = update_weights(components, data_input, method) + assert np.shape(actual) == (len(components), len(components[0].weights)) -@pytest.mark.parametrize("trs", trs) -def test_reconstruct_signal(trs): - actual = reconstruct_signal(trs[0], trs[1]) - assert len(actual) == len(trs[0][0].grid) +@pytest.mark.parametrize( + "components, expected", + [ + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + 1, + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 2, 2), + ], + 0, + ), + ( + [ + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 0), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 1), + ComponentSignal([0, 0.25, 0.5, 0.75, 1], 3, 2), + ], + 2, + ), + # ([ComponentSignal([0, .25, .5, .75, 1], 2, 0), ComponentSignal([0, .25, .5, .75, 1], 2, 1), + # ComponentSignal([0, .25, .5, .75, 1], 2, 2)], -1), + ], +) +def test_reconstruct_signal(components, expected): + actual = reconstruct_signal(components, expected) + assert len(actual) == len(components[0].grid)