Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
common/grad_descend.cpp
Go to the documentation of this file.
1 /*
2 Copyright 2020 Peter Rakyta, Ph.D.
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #define _USE_MATH_DEFINES
20 #include <math.h>
21 #include <cfloat>
22 #include <grad_descend.h>
23 
24 
31 Grad_Descend::Grad_Descend(void (* f_pointer) (Matrix_real, void *, double *, Matrix_real&), void* meta_data_in) {
32 
33  maximal_iterations = 5001;
35 
36  // numerical precision used in the calculations
37  num_precision = 1.42e-14;
38 
39 
40 
41  costfnc__and__gradient = f_pointer;
42  export_fnc = NULL;
43  meta_data = meta_data_in;
44 
45  // number of function calls during the optimization process
47 
48 }
49 
50 
57 Grad_Descend::Grad_Descend(void (* f_pointer) (Matrix_real, void *, double *, Matrix_real&), void (* export_pointer)(double , Matrix_real&, void* ), void* meta_data_in) {
58 
59  maximal_iterations = 5001;
61 
62  // numerical precision used in the calculations
63  num_precision = 1.42e-14;
64 
65 
66 
67  costfnc__and__gradient = f_pointer;
68  export_fnc = export_pointer;
69  meta_data = meta_data_in;
70 
71  // number of function calls during the optimization process
73 
74 }
75 
76 
77 
83 double Grad_Descend::Start_Optimization(Matrix_real &x, long maximal_iterations_in)
84 {
85 
86 
87 
88  variable_num = x.size();
89 
90 
91  // set the maximal number of iterations
92  maximal_iterations = maximal_iterations_in;
93 
95 
96 
97 
98 
99  // test for dimension
100  if ( variable_num <= 0 ) {
101  return DBL_MAX;
102  }
103 
104 
105  // Minimize the objective function
106  double f;
107  Optimize(x, f);
108 
109  return f;
110 
111 }
112 
113 
114 
115 
127 void Grad_Descend::line_search(Matrix_real& x, Matrix_real& g, Matrix_real& search_direction, Matrix_real& x0_search, Matrix_real& g0_search, double& maximal_step, double& d__dot__g0, double& f)
128 {
129 
130 
131 
132 
133  memcpy( x0_search.get_data(), x.get_data(), x.size()*sizeof(double) );
134  memcpy( g0_search.get_data(), g.get_data(), g.size()*sizeof(double) );
135 
136 
137  long max_loops = 50;//1 << 30;
138 
139 
140 
141  Matrix_real g_best = g.copy();
142 
143 
144  double step = std::min(1.0, maximal_step);
145 
146  double f_lowest = f;
147  double f_highest = f;
148 
149  double step_highest = maximal_step;
150  double step_lowest = 0.0;
151 
152  double step_best = 0.0;
153  double f_best = f;
154  double d__dot__g_abs_best = fabs(d__dot__g0);
155 
156  double d__dot__g_lowest = d__dot__g0;
157  double d__dot__g_highest = 0.0;
158 
159 
160  for( long iter_idx=0; iter_idx<max_loops; iter_idx++) {
161 
162  for (long idx = 0; idx < variable_num; idx++) {
163  x[idx] = x0_search[idx] + step * search_direction[idx];
164  }
165 
166 
167  get_f_ang_gradient(x, f, g);
168 
169  // overlap between the search direction and the gradient
170  double d__dot__g_current = 0.0;
171  for (long idx = 0; idx < variable_num; idx++) {
172  d__dot__g_current += search_direction[idx] * g[idx];
173  }
174 
175  // update best solution
176  if (f < f_best || fabs(d__dot__g_current) < d__dot__g_abs_best) {
177 
178  step_best = step;
179  f_best = f;
180  d__dot__g_abs_best = fabs(d__dot__g_current);
181 
182  memcpy( g_best.get_data(), g.get_data(), g.size()*sizeof(double) );
183 
184  }
185 
186  // exit the loop if maximal function calls reached
188  break;
189  }
190 
191 
192  // modify the upper and lower bound of the step inetrval
193  if (f < f_lowest + step * 0.1 * d__dot__g0) { // Armijo test (Wolfe 1st condition)
194 
195  if (d__dot__g_current >= d__dot__g0 * 0.7) { // Wolfe 2nd (curvature) condition to exit the iterations
196  break;
197  }
198 
199  step_lowest = step;
200  f_lowest = f;
201  d__dot__g_lowest = d__dot__g_current;
202 
203 
204  }
205  else {
206 
207  step_highest = step;
208  f_highest = f;
209  d__dot__g_highest = d__dot__g_current;
210 
211 
212  }
213 
214 
215 
216 
217 
218  // Calculate the next step length
219 
220  if (iter_idx == 0 || step_lowest > 0.0) {
221 
222  // It is critical to use the inexact line search (ILS) to ensure the global convergence of the nonlinear conjugate gradient (CG) method
223 
224  // fit a parabola a*step^2 + b*step + c to the points (step_lowest, f_lowest), (step_highest, f_highest) with a derivate d__dot__g_highest at step=step_highest
225  // then d__dot__g_lowest_fit is the derivate of the parabola at step=step_lowest
226  // f_highest = a*step_highest^2 + b*step_highest + c
227  // f_lowest = a*step_lowest^2 + b*step_lowest + c
228  double d__dot__g_lowest_fit = (f_highest - f_lowest) / (step_highest - step_lowest)*2.0 - d__dot__g_highest;
229 
230  // move on the parabola
231  double scale = 0.0;
232 
233  // the tip of the parabola is at step_lowest + scale * (step_highest - step_lowest)
234  scale = -d__dot__g_lowest_fit*0.5 / (d__dot__g_highest - d__dot__g_lowest_fit);
235  scale = std::max( 0.1, scale);
236 
237  step = step_lowest + scale * (step_highest - step_lowest);
238 
239  }
240  else {
241 
242  // In later BFGS iterations the minimum might be closer to the initial x0_search.
243  // Thus scaling down the step when step_lowest is still zero and the landscape near x0_serach need to be explored.
244  step = step * 0.1;
245 
246  if (step < num_precision) {
247  break;
248  }
249 
250  }
251 
252  } // for loop
253 
254 
255 
256  // copy the best solution in place of the current solution
257  if (step != step_best) {
258 
259  step = step_best;
260  f = f_best;
261 
262  g = g_best;
263 
264  for (long idx = 0; idx < variable_num; idx++) {
265  x[idx] = x0_search[idx] + step * search_direction[idx];
266  }
267  }
268 
269  if (step == 0.0 ) {
271  }
272 
273  return;
274 }
275 
276 
277 
278 
285 {
286 
287  // the initial gradient during the line search
288  Matrix_real g0_search( variable_num, 1 );
289 
290  // the initial point during the line search
291  Matrix_real x0_search( variable_num, 1 );
292 
293  // The calculated graient of the cost function at position x
294  Matrix_real g( variable_num, 1 );
295 
296  // the current search direction
297  Matrix_real search_direction( variable_num, 1 );
298 
300 
301 
302 
303  // inner product of the search direction and the gradient
304  double d__dot__g;
305 
306  double maximal_step; // The upper bound of the allowed step in the search direction
307 
309  get_f_ang_gradient(x, f, g);
310  }
311 
312  double fprev = fabs(f + f + 1.0);
313 
314  // Calculate the next search direction.
315 
316 
317  while ( true ) {
318 
319  maximal_step = 0.0;
320 
321  double search_direction__grad_overlap = 0.0;
322  get_search_direction(g, search_direction, search_direction__grad_overlap);
323 
324 
325  // Calculate the bound on the step-length
326 
327  if (search_direction__grad_overlap < 0.0) {
328  get_Maximal_Line_Search_Step(search_direction, maximal_step, search_direction__grad_overlap);
329  }
330  d__dot__g = search_direction__grad_overlap;
331 
332 
333 
334 
335  double gradient_norm = 0.0;
336  for (long idx = 0; idx < variable_num; idx++) {
337  gradient_norm += g[idx] * g[idx];
338  }
339 
340  double acc=1e-19;
341  // Test for convergence by measuring the magnitude of the gradient.
342  if (gradient_norm <= acc * acc) {
344  return;
345  }
346 
347  // in case the cost fuction does not show decrement in the direction of the line search
348  if (d__dot__g >= 0.0) {
350  return;
351  }
352 
353 
354  // terminate cycles if the cost function is not decreased any more
355  if (f >= fprev) {
357  return;
358  }
359 
360  fprev = f;
361 
362  if ( export_fnc ) {
363  export_fnc( f, x, meta_data );
364  }
365 
366 
367  // terminate if maximal number of iteration reached
370  return;
371  }
372 
373 
374  // perform line search in the direction search_direction
375  line_search(x, g, search_direction, x0_search, g0_search, maximal_step, d__dot__g, f);
376 
378  return;
379  }
380 
381 
382 
383  }
384 
385 
386 
387 
388 }
389 
390 
391 
392 
393 
394 
401 void Grad_Descend::get_Maximal_Line_Search_Step(Matrix_real& search_direction, double& maximal_step, double& search_direction__grad_overlap)
402 {
403 
404  // Set steps to constraint boundaries and find the least positive one.
405 
406 
407  maximal_step = 0.0;
408 
409  // the optimization landscape is periodic in 2PI
410  // the maximal step will be the 2PI step in the direction of the smallest component of the search direction
411  for( long kdx = 0; kdx < variable_num; kdx++ ) {
412 
413  // skip the current direction if it is too small
414  if ( fabs(search_direction[kdx]) < 1e-5 ) {
415  continue;
416  }
417 
418  double step_bound_tmp = std::abs(2*M_PI/search_direction[kdx]);
419 
420 
421 
422  if (maximal_step == 0.0 || step_bound_tmp < maximal_step) {
423 
424  maximal_step = step_bound_tmp;
425 
426  }
427 
428  }
429 
430  return;
431 
432 }
433 
434 
435 
436 
444 {
445 
448 
449  return;
450 }
451 
452 
453 
454 
461 void Grad_Descend::get_search_direction(Matrix_real& g, Matrix_real& search_direction, double& search_direction__grad_overlap )
462 {
463 
464 
465  // calculate the search direction d by d = -g
466  memset(search_direction.get_data(), 0.0, search_direction.size()*sizeof(double) );
467 
468  for (long row_idx = 0; row_idx < variable_num; row_idx++) {
469  search_direction[row_idx] = -g[row_idx];
470  }
471 
472 
473  // test variable to see downhill or uphill
474  search_direction__grad_overlap = 0.0;
475 
476  for (long idx = 0; idx < variable_num; idx++) {
477  search_direction__grad_overlap += search_direction[idx] * g[idx];
478  }
479 
480  return;
481 }
482 
483 
484 
489 
490 }
void * meta_data
additional data needed to evaluate the cost function
Definition: grad_descend.h:56
void get_f_ang_gradient(Matrix_real &x, double &f, Matrix_real &g)
Call this method to obtain the cost function and its gradient at a gives position given by x...
Matrix_real copy() const
Call to create a copy of the matrix.
void get_Maximal_Line_Search_Step(Matrix_real &search_direction, double &maximal_step, double &search_direction__grad_overlap)
Call this method to obtain the maximal step size during the line search.
long maximal_iterations
maximal count of iterations during the optimization
Definition: grad_descend.h:41
void line_search(Matrix_real &x, Matrix_real &g, Matrix_real &search_direction, Matrix_real &x0_search, Matrix_real &g0_search, double &maximal_step, double &d__dot__g, double &f)
Call to perform inexact line search terminated with Wolfe 1st and 2nd conditions. ...
scalar * get_data() const
Call to get the pointer to the stored data.
void(* export_fnc)(double, Matrix_real &, void *)
function pointer to evaluate the cost function and its gradient vector
Definition: grad_descend.h:53
double Start_Optimization(Matrix_real &x, long maximal_iterations_in=5001)
Call this method to start the optimization.
~Grad_Descend()
Destructor of the class.
virtual void Optimize(Matrix_real &x, double &f)
Call this method to start the optimization process.
enum solver_status status
status of the solver
Definition: grad_descend.h:59
long function_call_count
number of function calls during the optimization process
Definition: grad_descend.h:44
int size() const
Call to get the number of the allocated elements.
Grad_Descend(void(*f_pointer)(Matrix_real, void *, double *, Matrix_real &), void *meta_data_in)
Constructor of the class.
int variable_num
number of independent variables in the problem
Definition: grad_descend.h:38
double num_precision
numerical precision used in the calculations
Definition: grad_descend.h:47
virtual void get_search_direction(Matrix_real &g, Matrix_real &search_direction, double &search_direction__grad_overlap)
Method to get the search direction in the next line search.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
void(* costfnc__and__gradient)(Matrix_real x, void *params, double *f, Matrix_real &g)
function pointer to evaluate the cost function and its gradient vector
Definition: grad_descend.h:50