@@ -20,7 +20,7 @@ def __init__(
20
20
MM ,
21
21
Y0 = None ,
22
22
X0 = None ,
23
- A = None ,
23
+ A0 = None ,
24
24
rho = 1e12 ,
25
25
eta = 610 ,
26
26
max_iter = 500 ,
@@ -36,12 +36,12 @@ def __init__(
36
36
The data to be decomposed. Shape is (length_of_signal, number_of_conditions).
37
37
Y0 : ndarray
38
38
The initial guesses for the component weights at each stretching condition.
39
- Shape is (number of components, number ofconditions ) Must be provided if
40
- n_components is not provided. Will override n_components if both are provided .
39
+ Shape is (number_of_components, number_of_conditions ) Must provide exactly one
40
+ of this or n_components.
41
41
X0 : ndarray
42
42
The initial guesses for the intensities of each component per
43
43
row/sample/angle. Shape is (length_of_signal, number_of_components).
44
- A : ndarray
44
+ A0 : ndarray
45
45
The initial guesses for the stretching factor for each component, at each
46
46
condition. Shape is (number_of_components, number_of_conditions).
47
47
rho : float
@@ -60,41 +60,48 @@ def __init__(
60
60
objective function to allow without terminating the optimization. Note that
61
61
a minimum of 20 updates are run before this parameter is checked.
62
62
n_components : int
63
- The number of components to extract from MM. Note that this will
64
- be overridden by Y0 if that is provided, but must be provided if no Y0 is
65
- provided.
63
+ The number of components to extract from MM. Must be provided when and only when
64
+ Y0 is not provided.
66
65
random_state : int
67
66
The seed for the initial guesses at the matrices (A, X, and Y) created by
68
67
the decomposition.
69
68
"""
70
69
71
70
self .MM = MM
72
- self .X0 = X0
73
- self .Y0 = Y0
74
- self .A = A
75
71
self .rho = rho
76
72
self .eta = eta
77
73
# Capture matrix dimensions
78
74
self .N , self .M = MM .shape
79
75
self .num_updates = 0
80
76
self ._rng = np .random .default_rng (random_state )
81
77
78
+ # Enforce exclusive specification of n_components or Y0
79
+ if (n_components is None ) == (Y0 is not None ):
80
+ raise ValueError ("Must provide exactly one of Y0 or n_components, but not both." )
81
+
82
+ # Initialize Y0 and determine number of components
82
83
if Y0 is None :
83
- if n_components is None :
84
- raise ValueError ("Must provide either Y0 or n_components." )
85
- else :
86
- self .K = n_components
87
- self .Y0 = self ._rng .beta (a = 2.5 , b = 1.5 , size = (self .K , self .M ))
84
+ self .K = n_components
85
+ self .Y = self ._rng .beta (a = 2.5 , b = 1.5 , size = (self .K , self .M ))
88
86
else :
89
87
self .K = Y0 .shape [0 ]
88
+ self .Y = Y0
90
89
90
+ # Initialize A if not provided
91
91
if self .A is None :
92
92
self .A = np .ones ((self .K , self .M )) + self ._rng .normal (0 , 1e-3 , size = (self .K , self .M ))
93
- if self .X0 is None :
94
- self .X0 = self ._rng .random ((self .N , self .K ))
93
+ else :
94
+ self .A = A0
95
+
96
+ # Initialize X0 if not provided
97
+ if self .X is None :
98
+ self .X = self ._rng .random ((self .N , self .K ))
99
+ else :
100
+ self .X = X0
95
101
96
- self .X = np .maximum (0 , self .X0 )
97
- self .Y = np .maximum (0 , self .Y0 )
102
+ # Enforce non-negativity
103
+ self .X = np .maximum (0 , self .X )
104
+ self .Y = np .maximum (0 , self .Y )
98
105
99
106
# Second-order spline: Tridiagonal (-2 on diagonal, 1 on sub/superdiagonals)
100
107
self .P = 0.25 * diags ([1 , - 2 , 1 ], offsets = [0 , 1 , 2 ], shape = (self .M - 2 , self .M ))
0 commit comments