Skip to content

Commit 564213c

Browse files
committed
Merge branch 'main' into create_components
2 parents 8c510c6 + 2025d70 commit 564213c

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

diffpy/snmf/containers.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import numpy as np
2+
import numdifftools
3+
4+
5+
class ComponentSignal:
6+
"""
7+
Attributes
8+
----------
9+
grid: 1d array of floats
10+
The vector containing the grid points of the component.
11+
iq: 1d array of floats
12+
The intensity/g(r) values of the component.
13+
weights: 1d array of floats
14+
The vector containing the weight of the component signal for each signal.
15+
stretching_factors: 1d array of floats
16+
The vector containing the stretching factor for the component signal for each signal.
17+
id: int
18+
The number identifying the component.
19+
"""
20+
21+
def __init__(self, grid, number_of_signals, id_number, perturbation=1e-3):
22+
self.grid = np.asarray(grid)
23+
self.iq = np.random.rand(len(grid))
24+
self.weights = np.random.rand(number_of_signals)
25+
self.stretching_factors = np.ones(number_of_signals) + np.random.randn(number_of_signals) * perturbation
26+
self.id = int(id_number)
27+
28+
def apply_stretch(self, m):
29+
"""Applies a stretching factor to a component
30+
31+
Parameters
32+
----------
33+
m: int
34+
The index specifying which stretching factor to apply
35+
36+
Returns
37+
-------
38+
tuple of 1d arrays
39+
The tuple of vectors where one vector is the stretched component, one vector is the 1st derivative of the
40+
stretching operation, and one vector is the second derivative of the stretching operation.
41+
"""
42+
normalized_grid = np.arange(len(self.grid))
43+
func = lambda stretching_factor: np.interp(normalized_grid / stretching_factor, normalized_grid, self.iq,
44+
left=0, right=0)
45+
derivative_func = numdifftools.Derivative(func)
46+
second_derivative_func = numdifftools.Derivative(derivative_func)
47+
48+
stretched_component = func(self.stretching_factors[m])
49+
stretched_component_gra = derivative_func(self.stretching_factors[m])
50+
stretched_component_hess = second_derivative_func(self.stretching_factors[m])
51+
52+
return np.asarray(stretched_component), np.asarray(stretched_component_gra), np.asarray(
53+
stretched_component_hess)
54+
55+
def apply_weight(self, m, stretched_component=None):
56+
"""Applies as weight factor to a component signal.
57+
58+
Parameters
59+
----------
60+
m: int
61+
The index specifying with weight to apply
62+
stretched_component: 1d array
63+
The 1d array containing a stretched component.
64+
65+
Returns
66+
-------
67+
1d array
68+
The vector containing a component signal or stretched component signal with a weight factor applied.
69+
"""
70+
if stretched_component is None:
71+
return self.iq * self.weights[m]
72+
else:
73+
return stretched_component * self.weights[m]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import pytest
2+
import numpy as np
3+
from diffpy.snmf.containers import ComponentSignal
4+
5+
tas = [([np.arange(10), 3, 0, [6.55, .357, 8.49, 9.33, 6.78, 7.57, 7.43, 3.92, 6.55, 1.71], .25],
6+
[[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],
7+
[0, -19.92049156, 11.6931482, 0, 0, 0, 0, 0, 0, 0]]),
8+
([np.arange(5), 10, 0, [-11.47, -10.688, -8.095, -29.44, 14.38], 1.25],
9+
[[-11.47, -10.8444, -9.1322, -16.633, -20.6760], [0, -.50048, -3.31904, 40.9824, -112.1792],
10+
[0, .800768, 5.310464, -65.57184, 179.48672]]),
11+
([np.arange(5), 2, 0, [-11.47, -10.688, -8.095, -29.44, 14.38], .88],
12+
[[-11.47, -10.3344, -13.9164, -11.5136, 0], [0, -3.3484, 55.1265, -169.7572, 0],
13+
[0, 7.609997, -125.2876, 385.81189, 0]]),
14+
([np.arange(10), 1, 2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], .88],
15+
[[1, 2.1364, 3.2727, 4.4091, 5.5455, 6.6818, 7.8182, 8.9545, 0, 0],
16+
[0, -1.29, -2.58, -3.87, -5.165, -6.45, -7.74, -9.039, 0, 0],
17+
[0, 2.93, 5.869, 8.084, 11.739, 14.674, 17.608, 20.5437, 0, 0]]),
18+
([np.arange(14), 100, 3,
19+
[-2.9384, -1.4623, -2.0913, 4.6304, -1.2127, 1.4737, -0.3791, 1.7506, -1.5068, -2.7625, .9617, -.3494, -.3862,
20+
2.7960], .55], [[-2.9384, -1.9769, 0.9121, .6314, .8622, -2.4239, -.2302, 1.9281, 0, 0, 0, 0, 0, 0],
21+
[0, 2.07933, 38.632, 18.3748, 43.07305, -61.557, 26.005, -73.637, 0, 0, 0, 0, 0, 0],
22+
[0, -7.56, -140.480, -66.81, -156.6293, 223.84, -94.564, 267.7734, 0, 0, 0, 0, 0, 0]]),
23+
([np.arange(11), 20, 4, [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5], .987],
24+
[[0, .2533, .5066, .7599, 1.0132, 1.2665, 1.5198, 1.7730, 2.0263, 2.2796, 0],
25+
[0, -.2566, -.5132, -.7699, -1.0265, -1.2831, -1.5398, -1.7964, -2.0530, -2.3097, 0],
26+
[0, .5200, 1.0400, 1.56005, 2.08007, 2.6000, 3.1201, 3.6401, 4.1601, 4.6801, 0]]),
27+
([np.arange(9), 15, 3, [-1, -2, -3, -4, -5, -6, -7, -8, -9], -0.4],
28+
[[-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]])
29+
]
30+
@pytest.mark.parametrize('tas', tas)
31+
def test_apply_stretch(tas):
32+
component = ComponentSignal(tas[0][0], tas[0][1], tas[0][2])
33+
component.iq = tas[0][3]
34+
component.stretching_factors[0] = tas[0][4]
35+
actual = component.apply_stretch(0)
36+
expected = tas[1]
37+
np.testing.assert_allclose(actual, expected, rtol=1e-01)
38+
39+
40+
taw = [([np.arange(5), 2, 0, [0, 1, 2, 3, 4], .5], [0, .5, 1, 1.5, 2]),
41+
([np.arange(5), 20, 2, [0, -1, -2, -3, -4], .25], [0, -.25, -.5, -.75, -1]),
42+
([np.arange(40), 200, 4, np.arange(0, 10, .25), .3], np.arange(0, 10, .25) * .3),
43+
([np.arange(1), 10, 2, [10.5, 11.5, -10.5], 0], [0, 0, 0]),
44+
([[-12, -10, -15], 5, 2, [-.5, -1, -1.2], .9], [-.45, -.9, -1.08]),
45+
([[-12, -10, -15], 5, 2, [0, 0, 0], .9], [0, 0, 0])
46+
]
47+
@pytest.mark.parametrize('taw', taw)
48+
def test_apply_weight(taw):
49+
component = ComponentSignal(taw[0][0], taw[0][1], taw[0][2])
50+
component.iq = np.array(taw[0][3])
51+
component.weights[0] = taw[0][4]
52+
actual = component.apply_weight(0)
53+
expected = taw[1]
54+
np.testing.assert_allclose(actual, expected, rtol=1e-01)

0 commit comments

Comments
 (0)