11import numpy as np
22
3- from ._qr import QR
3+ from pysensors .optimizers ._qr import QR
4+
5+ import matplotlib .pyplot as plt
6+ from sklearn import datasets
7+ from sklearn import metrics
8+ from mpl_toolkits .axes_grid1 import make_axes_locatable
9+
10+ import pysensors as ps
411
512
613class GQR (QR ):
@@ -9,8 +16,15 @@ class GQR(QR):
916 Ranks sensors in descending order of "importance" based on
1017 reconstruction performance. This is an extension that requires a more intrusive
1118 access to the QR optimizer to facilitate a more adaptive optimization. This is a generalized version of cost constraints
12- in the sense that users can allow n consttrained sensors in the constrained area.
19+ in the sense that users can allow n constrained sensors in the constrained area.
1320 if n = 0 this converges to the CCQR results.
21+
22+ See the following reference for more information
23+ Manohar, Krithika, et al.
24+ "Data-driven sparse sensor placement for reconstruction:
25+ Demonstrating the benefits of exploiting known patterns."
26+ IEEE Control Systems Magazine 38.3 (2018): 63-86.
27+
1428 @ authors: Niharika Karnik (@nkarnik2999), Mohammad Abdo (@Jimmy-INL), and Krithika Manohar (@kmanohar)
1529 """
1630 def __init__ (self ,idx_constrained ,n_sensors ,const_sensors ):
@@ -19,6 +33,12 @@ def __init__(self,idx_constrained,n_sensors,const_sensors):
1933 ----------
2034 pivots_ : np.ndarray, shape [n_features]
2135 Ranked list of sensor locations.
36+ idx_constrained : np.ndarray, shape [No. of constrained locations]
37+ Column Indices of the sensors in the constrained locations.
38+ n_sensors : integer,
39+ Total number of sensors
40+ const_sensors : integer,
41+ Total number of sensors required by the user in the constrained region.
2242 """
2343 self .pivots_ = None
2444 self .constrainedIndices = idx_constrained
@@ -40,41 +60,34 @@ def fit(
4060
4161 Returns
4262 -------
43- self: a fitted :class:`pysensors.optimizers.CCQR ` instance
63+ self: a fitted :class:`pysensors.optimizers.QR ` instance
4464 """
4565
4666 n_features , n_samples = basis_matrix .shape # We transpose basis_matrix below
47- max_const_sensors = len (self .constrainedIndices )
67+ max_const_sensors = len (self .constrainedIndices ) #Maximum number of sensors allowed in the constrained region
4868
4969 ## Assertions and checks:
5070 if self .nSensors > n_features - max_const_sensors + self .nConstrainedSensors :
5171 raise IOError ("n_sensors cannot be larger than n_features - all possible locations in the constrained area + allowed constrained sensors" )
52- if self .nSensors > n_samples + self .nConstrainedSensors :
72+ if self .nSensors > n_samples + self .nConstrainedSensors : ## Handling zero constraint?
5373 raise IOError ("Currently n_sensors should be less than number of samples + number of constrained sensors,\
54- got: n_sensors = {}, n_samples + const_sensors = {} + {} = {}" .format (n_sensors ,n_samples ,const_sensors ,n_samples + const_sensors ))
74+ got: n_sensors = {}, n_samples + const_sensors = {} + {} = {}" .format (n_sensors ,n_samples ,self . nConstrainedSensors ,n_samples + self . nConstrainedSensors ))
5575
5676 # Initialize helper variables
5777 R = basis_matrix .conj ().T .copy ()
58- #print(R.shape)
5978 p = np .arange (n_features )
60- #print(p)
6179 k = min (n_samples , n_features )
6280
6381
6482 for j in range (k ):
6583 r = R [j :, j :]
6684 # Norm of each column
6785 dlens = np .sqrt (np .sum (np .abs (r ) ** 2 , axis = 0 ))
68-
69- # if j < const_sensors:
70- dlens_updated = f_region (self .constrainedIndices ,dlens ,p ,j , self .nConstrainedSensors )
71- # else:
72- # dlens_updated = dlens
86+ dlens_updated = f_region (self .constrainedIndices ,dlens ,p ,j , self .nConstrainedSensors ) #Handling constrained region sensor placement problem
87+
7388 # Choose pivot
7489 i_piv = np .argmax (dlens_updated )
75- #print(i_piv)
76-
77-
90+
7891 dlen = dlens_updated [i_piv ]
7992
8093 if dlen > 0 :
@@ -87,10 +100,7 @@ def fit(
87100
88101 # Track column pivots
89102 i_piv += j # true permutation index is i_piv shifted by the iteration counter j
90- # print(i_piv) # Niharika's debugging line
91103 p [[j , i_piv ]] = p [[i_piv , j ]]
92- # print(p)
93-
94104
95105 # Switch columns
96106 R [:, [j , i_piv ]] = R [:, [i_piv , j ]]
@@ -105,6 +115,7 @@ def fit(
105115 return self
106116
107117## TODO: why not a part of the class?
118+
108119#function for mapping sensor locations with constraints
109120def f_region (lin_idx , dlens , piv , j , const_sensors ):
110121 #num_sensors should be fixed for each custom constraint (for now)
@@ -115,10 +126,12 @@ def f_region(lin_idx, dlens, piv, j, const_sensors):
115126 Parameters
116127 ----------
117128 lin_idx: np.ndarray, shape [No. of constrained locations]
118- Array which contains the constrained locations mapped on the grid.
129+ Array which contains the constrained locationsof the grid in terms of column indices of basis_matrix .
119130 dlens: np.ndarray, shape [Variable based on j]
120131 Array which contains the norm of columns of basis matrix.
121- num_sensors: int,
132+ piv: np.ndarray, shape [n_features]
133+ Ranked list of sensor locations.
134+ const_sensors: int,
122135 Number of sensors to be placed in the constrained area.
123136 j: int,
124137 Iterative variable in the QR algorithm.
@@ -138,22 +151,43 @@ def f_region(lin_idx, dlens, piv, j, const_sensors):
138151 dlens [didx ] = 0
139152 return dlens
140153
141- def getConstraindSensorsIndices (xmin , xmax ,ymin ,ymax , all_sensors ):
154+ def getConstraindSensorsIndices (xmin , xmax , ymin , ymax , all_sensors ):
155+ """
156+ Function for mapping constrained sensor locations on the grid with the column indices of the basis_matrix.
157+
158+ Parameters
159+ ----------
160+ xmin: int,
161+ "Fill"
162+ xmax : int,
163+ "Fill"
164+ ymin : int,
165+ "Fill"
166+ ymax : int
167+ "Fill"
168+ all_sensors : np.ndarray, shape [n_features]
169+ Ranked list of sensor locations.
170+
171+ Returns
172+ -------
173+ idx_constrained : np.darray, shape [No. of constrained locations]
174+ Array which contains the constrained locationsof the grid in terms of column indices of basis_matrix.
175+ """
142176 n_features = len (all_sensors )
143177 imageSize = int (np .sqrt (n_features ))
144178 a = np .unravel_index (all_sensors , (imageSize ,imageSize ))
145179 constrained_sensorsx = []
146180 constrained_sensorsy = []
147181 for i in range (n_features ):
148- if (a [0 ][i ] > xmin and a [0 ][i ] < xmax ) and (a [1 ][i ] > ymin and a [1 ][i ] < ymax ): # x<10 and y>40
182+ if (a [0 ][i ] >= xmin and a [0 ][i ] <= xmax ) and (a [1 ][i ] >= ymin and a [1 ][i ] <= ymax ): # x<10 and y>40
149183 constrained_sensorsx .append (a [0 ][i ])
150184 constrained_sensorsy .append (a [1 ][i ])
151185
152186 constrained_sensorsx = np .array (constrained_sensorsx )
153187 constrained_sensorsy = np .array (constrained_sensorsy )
154188 constrained_sensors_array = np .stack ((constrained_sensorsy , constrained_sensorsx ), axis = 1 )
155189 constrained_sensors_tuple = np .transpose (constrained_sensors_array )
156- if len (constrained_sensorsx ) == 0 :
190+ if len (constrained_sensorsx ) == 0 : ##Check to handle condition when number of sensors in the constrained region = 0
157191 idx_constrained = []
158192 else :
159193 idx_constrained = np .ravel_multi_index (constrained_sensors_tuple , (imageSize ,imageSize ))
@@ -172,3 +206,88 @@ def functionalConstraint(position, func_response,func_input, freeTerm):
172206
173207if __name__ == '__main__' :
174208 pass
209+ faces = datasets .fetch_olivetti_faces (shuffle = True )
210+ X = faces .data
211+
212+ n_samples , n_features = X .shape
213+ print ('Number of samples:' , n_samples )
214+ print ('Number of features (sensors):' , n_features )
215+
216+ # Global centering
217+ X = X - X .mean (axis = 0 )
218+
219+ # Local centering
220+ X -= X .mean (axis = 1 ).reshape (n_samples , - 1 )
221+
222+ n_row , n_col = 2 , 3
223+ n_components = n_row * n_col
224+ image_shape = (64 , 64 )
225+
226+ def plot_gallery (title , images , n_col = n_col , n_row = n_row , cmap = plt .cm .gray ):
227+ '''Function for plotting faces'''
228+ plt .figure (figsize = (2. * n_col , 2.26 * n_row ))
229+ plt .suptitle (title , size = 16 )
230+ for i , comp in enumerate (images ):
231+ plt .subplot (n_row , n_col , i + 1 )
232+ vmax = max (comp .max (), - comp .min ())
233+ plt .imshow (comp .reshape (image_shape ), cmap = cmap ,
234+ interpolation = 'nearest' ,
235+ vmin = - vmax , vmax = vmax )
236+ plt .xticks (())
237+ plt .yticks (())
238+ plt .subplots_adjust (0.01 , 0.05 , 0.99 , 0.93 , 0.04 , 0. )
239+
240+ # plot_gallery("First few centered faces", X[:n_components])
241+
242+ #Find all sensor locations using built in QR optimizer
243+ max_const_sensors = 230
244+ n_const_sensors = 7
245+ n_sensors = 50
246+ optimizer = ps .optimizers .QR ()
247+ model = ps .SSPOR (optimizer = optimizer , n_sensors = n_sensors )
248+ model .fit (X )
249+
250+ all_sensors = model .get_all_sensors ()
251+
252+ ##Constrained sensor location on the grid:
253+ xmin = 20
254+ xmax = 40
255+ ymin = 25
256+ ymax = 45
257+ sensors_constrained = getConstraindSensorsIndices (xmin ,xmax ,ymin ,ymax ,all_sensors ) #Constrained column indices
258+
259+ ##Plotting the constrained region
260+ # ax = plt.subplot()
261+ # #Plot constrained space
262+ # img = np.zeros(n_features)
263+ # img[sensors_constrained] = 1
264+ # im = plt.imshow(img.reshape(image_shape),cmap=plt.cm.binary)
265+ # # create an axes on the right side of ax. The width of cax will be 5%
266+ # # of ax and the padding between cax and ax will be fixed at 0.05 inch.
267+ # divider = make_axes_locatable(ax)
268+ # cax = divider.append_axes("right", size="5%", pad=0.05)
269+ # plt.colorbar(im, cax=cax)
270+ # plt.title('Constrained region');
271+
272+ ## Fit the dataset with the optimizer GQR
273+ optimizer1 = GQR (sensors_constrained ,n_sensors ,n_const_sensors )
274+ model1 = ps .SSPOR (optimizer = optimizer1 , n_sensors = n_sensors )
275+ model1 .fit (X )
276+ all_sensors1 = model1 .get_all_sensors ()
277+
278+ top_sensors = model1 .get_selected_sensors ()
279+ print (top_sensors )
280+ ## TODO: this can be done using ravel and unravel more elegantly
281+ yConstrained = np .floor (top_sensors [:n_const_sensors ]/ np .sqrt (n_features ))
282+ xConstrained = np .mod (top_sensors [:n_const_sensors ],np .sqrt (n_features ))
283+
284+ img = np .zeros (n_features )
285+ img [top_sensors [n_const_sensors :]] = 16
286+ plt .plot (xConstrained ,yConstrained ,'*r' )
287+ plt .plot ([xmin ,xmin ],[ymin ,ymax ],'r' )
288+ plt .plot ([xmin ,xmax ],[ymax ,ymax ],'r' )
289+ plt .plot ([xmax ,xmax ],[ymin ,ymax ],'r' )
290+ plt .plot ([xmin ,xmax ],[ymin ,ymin ],'r' )
291+ plt .imshow (img .reshape (image_shape ),cmap = plt .cm .binary )
292+ plt .title ('n_sensors = {}, n_constr_sensors = {}' .format (n_sensors ,n_const_sensors ))
293+ plt .show ()
0 commit comments