Skip to content

Commit d526f5c

Browse files
authored
Merge pull request #25 from aajayi-21/lift_data
function lift_data and initialize_arrays
2 parents 1910311 + d3f7774 commit d526f5c

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

diffpy/snmf/subroutines.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,64 @@
44
import numdifftools
55

66

7+
def lift_data(data_input, lift=1):
8+
"""Lifts values of data_input
9+
10+
Adds 'lift' * the minimum value in data_input to data_input element-wise.
11+
12+
Parameters
13+
----------
14+
data_input: 2d array like
15+
The matrix containing a series of signals to be decomposed. Has dimensions N x M where N is the length of each
16+
signal and M is the number of signals.
17+
18+
lift: float
19+
The factor representing how much to lift 'data_input'.
20+
21+
Returns
22+
-------
23+
2d array like
24+
The matrix that contains data_input - (min(data_input) * lift).
25+
26+
"""
27+
data_input = np.asarray(data_input)
28+
return data_input + np.abs(np.min(data_input) * lift)
29+
30+
31+
def initialize_arrays(number_of_components, number_of_moments, signal_length):
32+
"""Generates the initial guesses for the weight, stretching, and component matrices
33+
34+
Calculates the initial guesses for the component matrix, stretching factor matrix, and weight matrix. The initial
35+
guess for the component matrix is a random (signal_length) x (number_of_components) matrix where each element is
36+
between 0 and 1. The initial stretching factor matrix is a random (number_of_components) x (number_of_moments)
37+
matrix where each element is number slightly perturbed from 1. The initial weight matrix guess is a random
38+
(number_of_components) x (number_of_moments) matrix where each element is between 0 and 1.
39+
40+
Parameters
41+
----------
42+
number_of_components: int
43+
The number of component signals to obtain from the stretched nmf decomposition.
44+
45+
number_of_moments: int
46+
The number of signals in the user provided dataset where each signal is at a different moment.
47+
48+
signal_length: int
49+
The length of each signal in the user provided dataset.
50+
51+
Returns
52+
-------
53+
tuple of 2d arrays of floats
54+
The tuple containing three elements: the initial component matrix guess, the initial stretching factor matrix
55+
guess, and the initial weight factor matrix guess in that order.
56+
57+
"""
58+
component_matrix_guess = np.random.rand(signal_length, number_of_components)
59+
weight_matrix_guess = np.random.rand(number_of_components, number_of_moments)
60+
stretching_matrix_guess = np.ones(number_of_components, number_of_moments) + np.random.randn(number_of_components,
61+
number_of_moments) * 1e-3
62+
return component_matrix_guess, weight_matrix_guess, stretching_matrix_guess
63+
64+
765
def objective_function(residual_matrix, stretching_factor_matrix, smoothness, smoothness_term, component_matrix,
866
sparsity):
967
"""Defines the objective function of the algorithm and returns its value.
@@ -80,6 +138,7 @@ def get_stretched_component(stretching_factor, component, signal_length):
80138

81139
def stretched_component_func(stretching_factor):
82140
return np.interp(normalized_grid / stretching_factor, normalized_grid, component, left=0, right=0)
141+
83142
derivative_func = numdifftools.Derivative(stretched_component_func)
84143
second_derivative_func = numdifftools.Derivative(derivative_func)
85144

diffpy/snmf/tests/test_subroutines.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import numpy as np
33
from diffpy.snmf.subroutines import objective_function, get_stretched_component, reconstruct_data, get_residual_matrix, \
4-
update_weights_matrix
4+
update_weights_matrix, initialize_arrays, lift_data
55

66
to = [
77
([[[1, 2], [3, 4]], [[5, 6], [7, 8]], 1e11, [[1, 2], [3, 4]], [[1, 2], [3, 4]], 1], 2.574e14),
@@ -134,3 +134,18 @@ def test_reconstruct_data(trd):
134134
actual = reconstruct_data(trd[0][0], trd[0][1], trd[0][2], trd[0][3], trd[0][4], trd[0][5])
135135
expected = trd[1]
136136
np.testing.assert_allclose(actual, expected, rtol=1e-03)
137+
138+
139+
tld = [(([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], 1]), ([[4, 2, 4], [3, 3, 3], [5, 13, 0]])),
140+
(([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], 0]), ([[1, -1, 1], [0, 0, 0], [2, 10, -3]])),
141+
(([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], .5]), ([[2.5, .5, 2.5], [1.5, 1.5, 1.5], [3.5, 11.5, -1.5]])),
142+
(([[[1, -1, 1], [0, 0, 0], [2, 10, -3]], -1]), ([[4, 2, 4], [3, 3, 3], [5, 13, 0]])),
143+
(([[[0, 0, 0], [0, 0, 0], [0, 0, 0]], 100]), ([[0, 0, 0], [0, 0, 0], [0, 0, 0]])),
144+
(([[[1.5, 2], [10.5, 1], [0.5, 2]], 1]), ([[2, 2.5], [11, 1.5], [1, 2.5]])),
145+
(([[[-10, -10.5], [-12.2, -12.2], [0, 0]], 1]), ([[2.2, 1.7], [0, 0], [12.2, 12.2]])),
146+
]
147+
@pytest.mark.parametrize('tld', tld)
148+
def test_lift_data(tld):
149+
actual = lift_data(tld[0][0], tld[0][1])
150+
expected = tld[1]
151+
np.testing.assert_allclose(actual, expected)

0 commit comments

Comments
 (0)