@@ -142,107 +142,13 @@ def after_optimization_sparsifier(current_measure, energy_curves=None):
142142 id2 = id1 + 1
143143 return output_measure
144144
145-
146- def measure_trimming (current_measure , energy_curves = None ):
147- """ Trim a sparse measure by decreasing the number of members.
148-
149- This function takes an input measure and the proceeds to delete replicated
150- atoms. If this is done in the insertion step, it furthers limits the number
151- of members if they exceed a preset value (defined in config.py as
152- curves_list_length_lim).
153- There are two execution cases:
154- case 1 - the vector energy_curves is provided, therefore it just checks for
155- duplicates, deletes it and return.
156- case 2 - the vector energy_curves is provided. In this case it is implicit
157- that the trimming is ocurring during the insertion step, therefore the
158- input measure is the union of both an original measure, with the stationary
159- points. The stationary points are those that get further trummed if their
160- energy (given by the energy_curves vector) is too low.
161-
162- :param current_measure: The target measure to be trimmed.
163- :type current_measure: class:`src.curves.measure`
164- :param energy_curves: list of F(γ) values for the curves obtained by the
165- `multistart_descent` method, defaults to None. :type energy_curves: list,
166- optional
167- """
168- # ---------------
169- # Inputs:
170- # current_measure (curves.measure type):
171- # the measure to be trimmed.
172- # Output:
173- # curves_list (list of curves.curve type object):
174- # The curves that survive the trimming. These are the atoms of the
175- # new measure.
176- # Kwargs:
177- # energy_curves (list of np.float, default None):
178- # If available, list of F(γ) values for the member curves of the
179- # set of stationary points. It accelerates comparisons.
180- # --------------
181- # Preset parameters:
182- # config.H1_tolerance
183- # config.curves_list_length_lim
184- curves_list = copy .deepcopy (current_measure .curves )
185- if energy_curves is None :
186- duplicates_idx = []
187- for i , curve1 in enumerate (curves_list ):
188- for curve2 in curves_list [i + 1 :]:
189- if (curve1 - curve2 ).H1_norm () < config .H1_tolerance :
190- duplicates_idx .append (i )
191- break
192- # take out the duplicated curves
193- for i in reversed (duplicates_idx ):
194- curves_list .pop (i )
195- else :
196- # Get the number of current curves and tabu curves
197- N_current_curves = len (curves_list )- len (energy_curves )
198- # separate curves
199- current_curves = curves_list [0 :N_current_curves ]
200- stationary_curves = curves_list [N_current_curves :]
201- # Order the tabu curves
202- sort_idx = np .argsort (energy_curves )
203- stationary_curves = [stationary_curves [i ] for i in sort_idx ]
204- energy_curves_list = [energy_curves [i ] for i in sort_idx ]
205- # Eliminate duplicate curves, using the information of the
206- # energy_curves (if possible) to accelerate this process
207- duplicates_idx = []
208- # The current curves should not be duplicated, we check with the
209- # tabu curves if they are duplicated.
210- for curve1 in current_curves :
211- for idx , curve2 in enumerate (stationary_curves ):
212- if (curve1 - curve2 ).H1_norm () < config .H1_tolerance :
213- duplicates_idx .append (idx )
214- ## eliminate duplicated idx's and sort them
215- duplicates_idx = list (dict .fromkeys (duplicates_idx ))
216- duplicates_idx .sort (reverse = True )
217- # remove the duplicate tabu curves
218- for i in duplicates_idx :
219- stationary_curves .pop (i )
220- energy_curves_list .pop (i )
221- print ("Eliminating duplicas, eliminated {} duplicate tabu curves" .format (
222- len (duplicates_idx )))
223- # Tabu curves should not be replicated given the Tabu search algorith.
224- # Now trim if the curves_list is too long
225- pop_counter = 0
226- while len (stationary_curves ) + N_current_curves > config .curves_list_length_lim and \
227- energy_curves_list [- 1 ] >= - 1 :
228- # find the one with least energy and pop it
229- stationary_curves .pop ()
230- energy_curves_list .pop ()
231- pop_counter += 1
232- if pop_counter > 0 :
233- print ("Trimming process: {} low energy tabu curves eliminated" .format (
234- pop_counter ))
235- curves_list = current_curves + stationary_curves # joining two lists
236- return curves_list
237-
238- def solve_quadratic_program (current_measure , energy_curves = None ):
145+ def solve_quadratic_program (current_measure ):
239146 assert isinstance (current_measure , curves .measure )
240147 # Build the quadratic system of step 5 and then use some generic python
241148 # solver to get a solution.
242149 # Build matrix Q and vector b
243150 logger = config .logger
244151 # First, check that no curves are duplicated
245- # curves_list = measure_trimming(current_measure, energy_curves=energy_curves)
246152 curves_list = current_measure .curves
247153 N = len (curves_list )
248154 Q = np .zeros ((N , N ), dtype = float )
@@ -262,20 +168,7 @@ def solve_quadratic_program(current_measure, energy_curves=None):
262168 # Theoretically, Q is positive semi-definite. Numerically, it might not.
263169 # We force Q to be positive semi-definite for the cvxopt solver to work
264170 # this is done simply by replacing the negative eigenvalues with 0
265- minEigVal = 0
266- eigval , eigvec = np .linalg .eigh (Q )
267- if min (eigval ) < minEigVal :
268- # truncate
269- print ("Negative eigenvalues: " ,
270- [eig for eig in eigval if eig < minEigVal ])
271- eigval = np .maximum (eigval , minEigVal )
272- # Recompute Q = VΣV^(-1)
273- Q2 = np .linalg .solve (eigvec .T , np .diag (eigval )@eigvec .T ).T
274- print ("PSD projection relative norm difference:" ,
275- np .linalg .norm (Q - Q2 )/ np .linalg .norm (Q ))
276- QQ = Q2
277- else :
278- QQ = Q
171+ QQ = to_positive_semidefinite (Q )
279172 try :
280173 Qc = cvxopt .matrix (QQ )
281174 bb = cvxopt .matrix (1 - b .reshape (- 1 , 1 ))
@@ -291,14 +184,38 @@ def solve_quadratic_program(current_measure, energy_curves=None):
291184 logger .status ([1 , 2 , 2 ], coefficients )
292185 return curves_list , coefficients
293186
294- def weight_optimization_step (current_measure , energy_curves = None ):
187+ def to_positive_semidefinite (Q ):
188+ """ Takes a symmetric matrix and returns a positive semidefinite projection
189+
190+ Parameters
191+ ----------
192+ Q : numpy.ndarray
193+ symmetric matrix
194+
195+ Returns
196+ -------
197+ numpy.ndarray, symmetric positive semidefinite matrix.
198+ """
199+ min_eigval = 0
200+ eigval , eigvec = np .linalg .eigh (Q )
201+ if min (eigval ) < min_eigval :
202+ # truncate
203+ print ("Negative eigenvalues: " ,
204+ [eig for eig in eigval if eig < min_eigval ])
205+ eigval = np .maximum (eigval , min_eigval )
206+ # Recompute Q = VΣV^(-1)
207+ Q2 = np .linalg .solve (eigvec .T , np .diag (eigval )@eigvec .T ).T
208+ print ("PSD projection relative norm difference:" ,
209+ np .linalg .norm (Q - Q2 )/ np .linalg .norm (Q ))
210+ return Q2
211+ return Q
212+
213+ def weight_optimization_step (current_measure ):
295214 config .logger .status ([1 , 2 , 1 ])
296215 # optimizes the coefficients for the current_measure
297216 # The energy_curves is a vector of energy useful for the trimming process
298217 assert isinstance (current_measure , curves .measure )
299- curves_list , coefficients = \
300- solve_quadratic_program (current_measure ,
301- energy_curves = energy_curves )
218+ curves_list , coefficients = solve_quadratic_program (current_measure )
302219 new_current_measure = curves .measure ()
303220 for curve , intensity in zip (curves_list , coefficients ):
304221 new_current_measure .add (curve , intensity )
@@ -316,7 +233,7 @@ def slide_and_optimize(current_measure):
316233 total_iterations = 0
317234 while total_iterations <= config .g_flow_opt_max_iter :
318235 current_measure , stepsize , iters = gradient_descent (current_measure ,
319- stepsize )
236+ stepsize )
320237 total_iterations += config .g_flow_opt_in_between_iters
321238 current_measure = weight_optimization_step (current_measure )
322239 if stepsize < config .g_flow_limit_stepsize :
@@ -330,7 +247,7 @@ def slide_and_optimize(current_measure):
330247 return current_measure
331248
332249def gradient_descent (current_measure , init_step ,
333- max_iter = config .g_flow_opt_in_between_iters ):
250+ max_iter = config .g_flow_opt_in_between_iters ):
334251 # We apply the gradient flow to simultaneously perturb the position of all
335252 # the current curves defining the measure.
336253 # Input: current_measure, measure type object.
0 commit comments