@@ -57,6 +57,9 @@ class SSPOR(BaseEstimator):
5757 ranked_sensors_: np.ndarray
5858 Sensor locations ranked in descending order of importance.
5959
60+ singular_values: np.ndarray
61+ Normalized singular values of the fitted data
62+
6063 Examples
6164 --------
6265 >>> import numpy as np
@@ -150,9 +153,8 @@ def fit(self, x, quiet=False, prefit_basis=False, seed=None, **optimizer_kws):
150153 # Check that n_sensors doesn't exceed dimension of basis vectors and
151154 # that it doesn't exceed the number of samples when using the CCQR optimizer.
152155 self ._validate_n_sensors ()
153- # Calculate the singular values
156+ # Calculate the normalized singular values
154157 X_proj = x @ self .basis_matrix_
155- # Normalized singular values
156158 self .singular_values = np .linalg .norm (X_proj , axis = 0 ) / np .sqrt (x .shape [0 ])
157159 # Find sparse sensor locations
158160 if isinstance (self .optimizer , TPGR ):
@@ -173,7 +175,7 @@ def fit(self, x, quiet=False, prefit_basis=False, seed=None, **optimizer_kws):
173175
174176 return self
175177
176- def predict (self , x , method = None , noise = None , prior = "decreasing" , ** solve_kws ):
178+ def predict (self , x , method = None , prior = "decreasing" , noise = None , ** solve_kws ):
177179 """
178180 Predict values at all positions given measurements at sensor locations.
179181
@@ -184,6 +186,21 @@ def predict(self, x, method=None, noise=None, prior="decreasing", **solve_kws):
184186 The measurements should be taken at the sensor locations specified by
185187 ``self.get_selected_sensors()``.
186188
189+ method : {'unregularized', None}, optional
190+ If None (default), performs regularized reconstruction using the prior
191+ and noise. If 'unregularized', uses direct or least-squares inversion
192+ depending on matrix shape.
193+
194+ prior: str or np.ndarray, shape (n_basis_modes,), optional
195+ (default='decreasing')
196+ Prior Covariance Vector, typically a scaled identity vector or a vector
197+ containing normalized singular values. If 'decreasing', normalized singular
198+ values are used.
199+
200+ noise: float (default None)
201+ Magnitude of the gaussian uncorrelated sensor measurement noise.
202+ If None, noise will default to the average of the computed prior.
203+
187204 solve_kws: dict, optional
188205 keyword arguments to be passed to the linear solver used to invert
189206 the basis matrix.
@@ -202,9 +219,11 @@ def predict(self, x, method=None, noise=None, prior="decreasing", **solve_kws):
202219 # Although if the user changes the number of sensors between calls
203220 # the factorization will be wasted.
204221
205- if self .n_sensors > self .basis_matrix_ .shape [0 ]:
222+ if self .n_sensors > self .basis_matrix_ .shape [0 ] and method == "unregularized" :
206223 warnings .warn (
207- "n_sensors exceeds dimension of basis modes. Performance may be poor"
224+ "n_sensors exceeds dimension of basis modes. Performance may be poor "
225+ "for unregularized reconstruction. Consider using regularized "
226+ "reconstruction"
208227 )
209228
210229 if method is None :
@@ -219,10 +238,15 @@ def predict(self, x, method=None, noise=None, prior="decreasing", **solve_kws):
219238 f" but got { prior .shape } "
220239 )
221240 computed_prior = prior
241+ else :
242+ raise ValueError (
243+ "Invalid prior: must be 'decreasing' or a 1D "
244+ "ndarray of appropriate length."
245+ )
222246 if noise is None :
223247 warnings .warn (
224248 "noise is None. noise will be set to the "
225- "average of the normalized prior"
249+ "average of the computed prior"
226250 )
227251 noise = computed_prior .mean ()
228252 return self ._regularized_reconstruction (x , computed_prior , noise )
@@ -259,10 +283,6 @@ def _regularized_reconstruction(self, x, prior, noise):
259283 noise: float (default None)
260284 Magnitude of the gaussian uncorrelated sensor measurement noise
261285 """
262- if noise is None :
263- noise = 1
264- if not isinstance (prior , np .ndarray ):
265- raise ValueError ("prior must be a numpy array" )
266286 prior_cov = 1 / (prior ** 2 )
267287 low_rank_selection_matrix = self .basis_matrix_ [self .selected_sensors , :]
268288 composite_matrix = np .diag (prior_cov ) + (
@@ -588,12 +608,15 @@ def std(self, prior, noise=None):
588608
589609 Parameters
590610 ----------
591- prior: np.ndarray (n_basis_modes,)
611+ prior: str or np.ndarray, shape (n_basis_modes,), optional
612+ (default='decreasing')
592613 Prior Covariance Vector, typically a scaled identity vector or a vector
593- containing normalized sigular values.
614+ containing normalized singular values. If 'decreasing', normalized singular
615+ values are used.
594616
595617 noise: float (default None)
596618 Magnitude of the gaussian uncorrelated sensor measurement noise.
619+ If None, noise will default to the average of the computed prior.
597620
598621 Returns
599622 -------
@@ -602,10 +625,6 @@ def std(self, prior, noise=None):
602625
603626 """
604627 check_is_fitted (self , "basis_matrix_" )
605- if noise is None :
606- noise = 1
607- if noise <= 0 :
608- raise ValueError ("Noise must be positive" )
609628 if isinstance (prior , str ) and prior == "decreasing" :
610629 computed_prior = self .singular_values
611630 elif isinstance (prior , np .ndarray ):
@@ -617,6 +636,16 @@ def std(self, prior, noise=None):
617636 f" but got { prior .shape } "
618637 )
619638 computed_prior = prior
639+ else :
640+ raise ValueError (
641+ "Invalid prior: must be 'decreasing' or a 1D "
642+ "ndarray of appropriate length."
643+ )
644+ if noise is None :
645+ warnings .warn (
646+ "noise is None. noise will be set to the average of the computed prior"
647+ )
648+ noise = computed_prior .mean ()
620649 sq_inv_prior = 1.0 / (computed_prior ** 2 )
621650 low_rank_selection_matrix = self .basis_matrix_ [self .selected_sensors , :]
622651 composite_matrix = np .diag (sq_inv_prior ) + (
@@ -632,7 +661,35 @@ def std(self, prior, noise=None):
632661 return sigma
633662
634663 def one_pt_energy_landscape (self , prior = "decreasing" , noise = None ):
635- check_is_fitted (self , "optimizer" )
664+ """
665+ Compute the one-point energy landscape of the sensors
666+
667+ See the following reference for more information
668+
669+ Klishin, Andrei A., et. al.
670+ Data-Induced Interactions of Sparse Sensors. 2023.
671+ arXiv:2307.11838 [cond-mat.stat-mech]
672+
673+ Parameters
674+ ----------
675+ prior: str or np.ndarray, shape (n_basis_modes,), optional
676+ (default='decreasing')
677+ Prior Covariance Vector, typically a scaled identity vector or a vector
678+ containing normalized singular values. If 'decreasing', normalized singular
679+ values are used.
680+
681+ noise: float (default None)
682+ Magnitude of the gaussian uncorrelated sensor measurement noise.
683+ If None, noise will default to the average of the computed prior.
684+
685+ Returns
686+ -------
687+ np.ndarray, shape (n_features,)
688+ """
689+ if isinstance (self .optimizer , TPGR ):
690+ check_is_fitted (self , "optimizer" )
691+ else :
692+ "Energy landscapes can only be computed if TPGR optimizer is used."
636693 if isinstance (prior , str ) and prior == "decreasing" :
637694 computed_prior = self .singular_values
638695 elif isinstance (prior , np .ndarray ):
@@ -644,16 +701,56 @@ def one_pt_energy_landscape(self, prior="decreasing", noise=None):
644701 f" but got { prior .shape } "
645702 )
646703 computed_prior = prior
704+ else :
705+ raise ValueError (
706+ "Invalid prior: must be 'decreasing' or a 1D "
707+ "ndarray of appropriate length."
708+ )
647709 if noise is None :
648710 warnings .warn (
649- "noise is None. noise will be set to the "
650- "average of the normalized prior"
711+ "noise is None. noise will be set to the average of the computed prior"
651712 )
652713 noise = computed_prior .mean ()
653714 G = self .basis_matrix_ @ np .diag (computed_prior )
654715 return - np .log (1 + np .einsum ("ij,ij->i" , G , G ) / noise ** 2 )
655716
656717 def two_pt_energy_landscape (self , selected_sensors , prior = "decreasing" , noise = None ):
718+ """
719+ Compute the two-point energy landscape of the sensors. If selected_sensors is a
720+ singular sensor, the landscape will be the two point energy interations of that
721+ sensor with the remaining sensors. If selected_sensors is a list of sensors,
722+ the landscape will be the sum of two point energy interactions of the selected
723+ sensors with the remaining sensors.
724+
725+ See the following reference for more information
726+
727+ Klishin, Andrei A., et. al.
728+ Data-Induced Interactions of Sparse Sensors. 2023.
729+ arXiv:2307.11838 [cond-mat.stat-mech]
730+
731+ Parameters
732+ ----------
733+ prior: str or np.ndarray, shape (n_basis_modes,), optional
734+ (default='decreasing')
735+ Prior Covariance Vector, typically a scaled identity vector or a vector
736+ containing normalized singular values. If 'decreasing', normalized singular
737+ values are used.
738+
739+ noise: float (default None)
740+ Magnitude of the gaussian uncorrelated sensor measurement noise.
741+ If None, noise will default to the average of the computed prior.
742+
743+ selected_sensors: list
744+ Indices of selected sensors for two point energy computation.
745+
746+ Returns
747+ -------
748+ np.ndarray, shape (n_features,)
749+ """
750+ if isinstance (self .optimizer , TPGR ):
751+ check_is_fitted (self , "optimizer" )
752+ else :
753+ "Energy landscapes can only be computed if TPGR optimizer is used."
657754 check_is_fitted (self , "optimizer" )
658755 if isinstance (prior , str ) and prior == "decreasing" :
659756 computed_prior = self .singular_values
@@ -666,10 +763,14 @@ def two_pt_energy_landscape(self, selected_sensors, prior="decreasing", noise=No
666763 f" but got { prior .shape } "
667764 )
668765 computed_prior = prior
766+ else :
767+ raise ValueError (
768+ "Invalid prior: must be 'decreasing' or a 1D "
769+ "ndarray of appropriate length."
770+ )
669771 if noise is None :
670772 warnings .warn (
671- "noise is None. noise will be set to the "
672- "average of the normalized prior"
773+ "noise is None. noise will be set to the average of the computed prior"
673774 )
674775 noise = computed_prior .mean ()
675776 G = self .basis_matrix_ @ np .diag (computed_prior )
0 commit comments