"""Main solver interface for Quantum Signal Processing optimization.
This module provides the main interface for solving QSP optimization problems,
coordinating the various optimization methods and utility functions.
"""
import numpy as np
from time import time
from .utils import chebyshev_to_func, reduced_to_full
from .objective import obj_sym, grad_sym, grad_sym_real
from .optimizers import lbfgs, coordinate_minimization, newton, nlft
[docs]def solve(coef, parity, opts):
"""Given coefficients of a polynomial P, yield corresponding phase factors.
The reference chose the first half of the phase factors as the
optimization variables, while in the code we used the second half of the
phase factors. These two formulations are equivalent.
To simplify the representation, a constant pi/4 is added to both sides of
the phase factors when evaluating the objective and the gradient. In the
output, the FULL phase factors with pi/4 are given.
Parameters
----------
coef : array_like
Coefficients of polynomial P under Chebyshev basis. P should be even/odd,
only provide non-zero coefficients. Coefficients should be ranked from
low order term to high order term.
parity : int
Parity of polynomial P (0 -- even, 1 -- odd)
opts : dict, optional
Options dictionary with fields:
- criteria : float
Stop criteria
- useReal : bool
Use only real arithmetics if true
- targetPre : bool
Want Pre to be target function if true
- method : {'LBFGS', 'FPI', 'Newton'}
Optimization method to use
- typePhi : {'full', 'reduced'}
Type of phase factors to return
Returns
-------
phi_proc : ndarray
Solution of optimization problem, FULL phase factors
out : dict
Information of solving process containing:
- iter : int
Number of iterations
- time : float
Runtime in seconds
- value : float
Final error value
- parity : int
Input parity value
- targetPre : bool
Whether Pre was target function
- typePhi : str
Type of phase factors returned
"""
# Setup options for L-BFGS solver
opts.setdefault('maxiter', 5e4)
opts.setdefault('criteria', 1e-12)
opts.setdefault('useReal', True)
opts.setdefault('targetPre', True)
opts.setdefault('method', 'FPI')
opts.setdefault('typePhi', 'full')
if opts['method'] == 'LBFGS':
# Initial preparation
tot_len = len(coef)
delta = np.cos((np.arange(1, 2 * tot_len, 2) * (np.pi / (2 * tot_len))))
if not opts['targetPre']:
opts['target'] = lambda x: -chebyshev_to_func(x, coef, parity, True)
else:
opts['target'] = lambda x: chebyshev_to_func(x, coef, parity, True)
opts['parity'] = parity
obj = obj_sym
grad = grad_sym_real if opts['useReal'] else grad_sym
# Solve by L-BFGS with selected initial point
start_time = time()
phi, err, iter = lbfgs(obj, grad, delta, np.zeros(tot_len), opts)
# Convert phi to reduced phase factors
if parity == 0:
phi[0] = phi[0] / 2
runtime = time() - start_time
elif opts['method'] == 'FPI':
phi, err, iter, runtime = coordinate_minimization(coef, parity, opts)
elif opts['method'] == 'Newton':
phi, err, iter, runtime = newton(coef, parity, opts)
elif opts['method'] == 'NLFT':
phi, err, iter, runtime = nlft(coef, parity, opts)
else:
print("Assigned method doesn't exist. Please choose method from 'LBFGS', 'FPI' or 'Newton'.")
return None, None
# Output information
out = {
'iter': iter,
'time': runtime,
'value': err,
'parity': parity,
'targetPre': opts['targetPre']
}
if opts['typePhi'] == 'full':
phi_proc = reduced_to_full(phi, parity, opts['targetPre'])
out['typePhi'] = 'full'
else:
phi_proc = phi
out['typePhi'] = 'reduced'
return phi_proc, out