Skip to content

Commit

Permalink
Updated README to reflect new and deprecated argument options, and de…
Browse files Browse the repository at this point in the history
…bugged small aspects of completion methods.
  • Loading branch information
white-noise committed Oct 23, 2024
1 parent 9b6c876 commit 37040dd
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 46 deletions.
55 changes: 31 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,56 +337,65 @@ This as well as many other polynomial families given in `pyqsp/poly.py` allow fo
A wide selection of the functionalities provided by this package can also be run from the command line using a series of arguments and flags. We detail these below, noting that there exist new methods under active development not covered here, though these changes will be backwards compatible to the methods given below.

```bash
usage: pyqsp [-h] [-v] [-o OUTPUT] [--signal_operator SIGNAL_OPERATOR] [--plot] [--hide-plot] [--return-angles] [--poly POLY] [--func FUNC]
[--polydeg POLYDEG] [--tau TAU] [--epsilon EPSILON] [--seqname SEQNAME] [--seqargs SEQARGS] [--polyname POLYNAME] [--polyargs POLYARGS]
[--plot-magnitude] [--plot-probability] [--plot-real-only] [--title TITLE] [--measurement MEASUREMENT] [--output-json]
[--plot-positive-only] [--plot-tight-y] [--plot-npts PLOT_NPTS] [--tolerance TOLERANCE] [--method METHOD] [--plot-qsp-model]
[--phiset PHISET] [--nepochs NEPOCHS] [--npts-theta NPTS_THETA]
usage: pyqsp [-h] [-v] [-o OUTPUT] [--signal_operator SIGNAL_OPERATOR] [--plot] [--hide-plot] [--return-angles] [--poly POLY] [--func FUNC] [--polydeg POLYDEG] [--scale SCALE]
[--seqname SEQNAME] [--seqargs SEQARGS] [--polyname POLYNAME] [--polyargs POLYARGS] [--plot-magnitude] [--plot-probability] [--plot-real-only] [--title TITLE]
[--measurement MEASUREMENT] [--output-json] [--plot-positive-only] [--plot-tight-y] [--plot-npts PLOT_NPTS] [--tolerance TOLERANCE] [--method METHOD]
[--plot-qsp-model] [--phiset PHISET] [--nepochs NEPOCHS] [--npts-theta NPTS_THETA]
cmd

usage: pyqsp [options] cmd

Version: 0.1.4
Version: 0.1.6
Commands:

poly2angles - compute QSP phase angles for the specified polynomial (use --poly)
hamsim - compute QSP phase angles for Hamiltonian simulation using the Jacobi-Anger expansion of exp(-i tau sin(2 theta))
invert - compute QSP phase angles for matrix inversion, i.e. a polynomial approximation to 1/a, for given delta and epsilon parameter values
angles - generate QSP phase angles for the specified --seqname and --seqargs
poly - generate QSP phase angles for the specified --polyname and --polyargs, e.g. sign and threshold polynomials
polyfunc - generate QSP phase angles for the specified --func and --polydeg using tensorflow + keras optimization method (--tf)
polyfunc - generate QSP phase angles for the specified --func and --polydeg using tensorflow + keras optimization method (--tf). Note that the function appears as the top left element of the resulting unitary.
sym_qsp_func - generate QSP phase angles for the specified --func and --polydeg using symmetric QSP iterative method. Note that the desired polynomial is automatically rescaled (unless a scalar --scale less than one is specified), and appears as the imaginary part of the top-left unitary matrix element in the standard basis.
response - generate QSP polynomial response functions for the QSP phase angles specified by --phiset

Examples:

# Simple examples for determining QSP phases from poly coefs.
pyqsp --poly=-1,0,2 poly2angles
pyqsp --poly=-1,0,2 --plot poly2angles
pyqsp --signal_operator=Wz --poly=0,0,0,1 --plot poly2angles
pyqsp --plot --tau 10 hamsim
pyqsp --plot --tolerance=0.01 --seqargs 3 invert
pyqsp --signal_operator=Wz --poly=0,0,0,1 --plot poly2angles

# Note examples using the 'sym_qsp' method.
pyqsp --plot --seqargs=10,0.1 --method sym_qsp hamsim
pyqsp --plot --seqargs=19,10 --method sym_qsp poly_sign
pyqsp --plot --seqargs=19,0.25 --method sym_qsp poly_linear_amp
pyqsp --plot --seqargs=68,20 --method sym_qsp poly_phase
pyqsp --plot --seqargs=20,0.6,15 --method sym_qsp relu
pyqsp --plot --seqargs=3,0.1 --method sym_qsp invert
pyqsp --plot --func "np.sign(x)" --polydeg 151 --scale 0.5 sym_qsp_func
pyqsp --plot --func "np.sin(10*x)" --polydeg 31 sym_qsp_func
pyqsp --plot --func "np.sign(x - 0.5) + np.sign(-1*x - 0.5)" --polydeg 151 --scale 0.9 sym_qsp_func

# Note older examples using the 'laurent' method.
pyqsp --plot --tolerance=0.1 --seqargs 2 invert
pyqsp --plot-npts=4000 --plot-positive-only --plot-magnitude --plot --seqargs=1000,1.0e-20 --seqname fpsearch angles
pyqsp --plot-npts=100 --plot-magnitude --plot --seqargs=23 --seqname erf_step angles
pyqsp --plot-npts=100 --plot-positive-only --plot --seqargs=23 --seqname erf_step angles
pyqsp --plot-real-only --plot --polyargs=20,20 --polyname poly_thresh poly
pyqsp --plot-positive-only --plot --polyargs=19,10 --plot-real-only --polyname poly_sign poly
pyqsp --plot-positive-only --plot-real-only --plot --polyargs 20,3.5 --polyname gibbs poly
pyqsp --plot-positive-only --plot-real-only --plot --polyargs 20,0.2,0.9 --polyname efilter poly

# Note older examples using the deprecated 'tf' method.
pyqsp --plot-positive-only --plot --polyargs=19,10 --plot-real-only --polyname poly_sign --method tf poly
pyqsp --plot --func "np.cos(3*x)" --polydeg 6 polyfunc
pyqsp --plot --func "np.cos(3*x)" --polydeg 6 --plot-qsp-model polyfunc
pyqsp --plot-positive-only --plot-real-only --plot --polyargs 20,3.5 --polyname gibbs --plot-qsp-model poly
pyqsp --polydeg 16 --measurement="z" --func="-1+np.sign(1/np.sqrt(2)-x)+ np.sign(1/np.sqrt(2)+x)" --plot polyfunc

# Note new techniques using --method sym_qsp, and sym_qsp_func.

pyqsp --plot --seqargs=10,0.1 --method sym_qsp hamsim
pyqsp --plot --seqargs=19,10 --method sym_qsp poly_sign
pyqsp --plot --func "np.cos(3*x)" --polydeg 6 sym_qsp_func

positional arguments:
cmd command

optional arguments:
options:
-h, --help show this help message and exit
-v, --verbose increase output verbosity (add more -v to increase versbosity)
-o OUTPUT, --output OUTPUT
Expand All @@ -396,14 +405,13 @@ optional arguments:
--plot generate QSP response plot
--hide-plot do not show plot (but it may be saved to a file if --output is specified)
--return-angles return QSP phase angles to caller
--poly POLY comma delimited list of floating-point coeficients for polynomial, as const, a, a^2, ...
--poly POLY comma delimited list of floating-point coefficients for polynomial, as const, a, a^2, ...
--func FUNC for tf method, numpy expression specifying ideal function (of x) to be approximated by a polynomial, e.g. 'np.cos(3 * x)'
--polydeg POLYDEG for tf method, degree of polynomial to use in generating approximation of specified function (see --func)
--tau TAU time value for Hamiltonian simulation (hamsim command)
--epsilon EPSILON parameter for polynomial approximation to 1/a, giving bound on error
--scale SCALE Within 'sym_qsp' method, specifies a float to which the extreme point of the approximating polynomial is rescaled in absolute value. For highly oscillatory functions, try decreasing this parameter toward zero.
--seqname SEQNAME name of QSP phase angle sequence to generate using the 'angles' command, e.g. fpsearch
--seqargs SEQARGS arguments to the phase angles generated by seqname (e.g. length,delta,gamma for fpsearch)
--polyname POLYNAME name of polynomial generate using the 'poly' command, e.g. 'sign'
--polyname POLYNAME name of polynomial generated using the 'poly' command, e.g. 'sign'
--polyargs POLYARGS arguments to the polynomial generated by poly (e.g. degree,kappa for 'sign')
--plot-magnitude when plotting only show magnitude, instead of separate imaginary and real components
--plot-probability when plotting only show squared magnitude, instead of separate imaginary and real components
Expand All @@ -418,13 +426,12 @@ optional arguments:
number of points to use in plotting
--tolerance TOLERANCE
error tolerance for phase angle optimizer
--method METHOD method to use for qsp phase angle generation, one among 'laurent' (default), 'tf' (for tensorflow + keras), of 'sym_qsp' for symmetric quantum signal processing
--method METHOD method to use for qsp phase angle generation, either 'laurent' (default), 'sym_qsp' for iterative methods involving symmetric QSP, or 'tf' (for tensorflow + keras)
--plot-qsp-model show qsp_model version of response plot instead of the default plot
--phiset PHISET comma delimited list of QSP phase angles, to be used in the 'response' command
--nepochs NEPOCHS number of epochs to use in tensorflow optimization
--npts-theta NPTS_THETA
number of discretized values of theta to use in tensorflow optimization

number of discretized values of theta to use in TensorFlow optimization
```
<!-- *TODO: INCLUDE A SORT OF GALLERY OF COMMON EXAMPLES, ALONG WITH EXPECTED PARAMETERS AND OUTPUT PLOTS, ETC.* -->
Expand Down
24 changes: 20 additions & 4 deletions pyqsp/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,22 +127,35 @@ def _fg_completion(F, seed):
# Find all the roots of Id - (p * ~p) that lies within the upper unit
# circle.
poly = (Id - (F * ~F)).coefs
roots = np.roots(poly)
converted_poly = np.polynomial.Polynomial(poly)
roots = converted_poly.roots()

# poly is a real, self-inverse Laurent polynomial with no root on the unit circle. All real roots come in reciprocal pairs,
# and all complex roots come in quadruples (r, r*, 1/r, 1/r*).
# For each pair of real roots, select the one within the unit circle.
# For each quadruple of complex roots, select the pair within the unit
# circle.
imag_roots = []
real_roots = []
# TODO: print("ROOTS")
for i in roots:
if (np.abs(i) < 1) and (np.imag(i) > -1e-8):
if (np.abs(i) < 1) and (np.imag(i) > -1e-14):
# TODO: print(f"ROOT: {i}")
if np.imag(i) == 0.:
real_roots.append(np.real(i))
else:
imag_roots.append(i)

# TODO: print(len(roots))
# TODO: print(len(imag_roots))
# TODO: print(len(real_roots))

norm = poly[-1]

"""
As it stands, the root randomization below seems to have a large and unstable effect on completion.
"""

# Randomly choose whether to pick the real root (the pair of complex roots)
# inside or outside the unit circle.
# This is to reduce the range of the coefficients appearing in the final
Expand All @@ -152,11 +165,11 @@ def _fg_completion(F, seed):
if seed is None:
seed = np.random.randint(2, size=len(imag_roots) + len(real_roots))
for i, root in enumerate(imag_roots):
if seed[i]:
if 1: # seed[i]:
root = 1 / root
lst.append(LPoly([np.abs(root) ** 2, -2 * np.real(root), 1]))
for i, root in enumerate(real_roots):
if seed[i + len(imag_roots)]:
if 1: # seed[i + len(imag_roots)]:
root = 1 / root
lst.append(LPoly([-root, 1]))

Expand All @@ -168,6 +181,9 @@ def _fg_completion(F, seed):
gcoefs = np.real(np.fft.fft(coef_fft, 1 << pp))[
:degree + 1] / (1 << pp)

# TODO: print(f"NORM: {norm}")
# TODO: print(f"GCOEF: {gcoefs[0]}") # Growing too large at high degree.

# Normalization
G = LPoly(gcoefs * np.sqrt(norm / gcoefs[0]), -len(gcoefs) + 1)

Expand Down
61 changes: 43 additions & 18 deletions pyqsp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,33 +40,39 @@ def CommandLine(args=None, arglist=None):
invert - compute QSP phase angles for matrix inversion, i.e. a polynomial approximation to 1/a, for given delta and epsilon parameter values
angles - generate QSP phase angles for the specified --seqname and --seqargs
poly - generate QSP phase angles for the specified --polyname and --polyargs, e.g. sign and threshold polynomials
polyfunc - generate QSP phase angles for the specified --func and --polydeg using tensorflow + keras optimization method (--tf)
sym_qsp_func - generate QSP phase angles for the specified --func and --polydeg using symmetric QSP iterative method. Note that the desired polynomial is automatically scaled, and appears as the imaginary part of the top-left unitary matrix element in the standard basis.
polyfunc - generate QSP phase angles for the specified --func and --polydeg using tensorflow + keras optimization method (--tf). Note that the function appears as the top left element of the resulting unitary.
sym_qsp_func - generate QSP phase angles for the specified --func and --polydeg using symmetric QSP iterative method. Note that the desired polynomial is automatically rescaled (unless a scalar --scale less than one is specified), and appears as the imaginary part of the top-left unitary matrix element in the standard basis.
response - generate QSP polynomial response functions for the QSP phase angles specified by --phiset
Examples:
# Simple examples for determining QSP phases from poly coefs.
pyqsp --poly=-1,0,2 poly2angles
pyqsp --poly=-1,0,2 --plot poly2angles
pyqsp --signal_operator=Wz --poly=0,0,0,1 --plot poly2angles
pyqsp --plot --tau 10 hamsim
# Note examples using the new sym_qsp method
# Note examples using the 'sym_qsp' method.
pyqsp --plot --seqargs=10,0.1 --method sym_qsp hamsim
pyqsp --plot --seqargs=19,10 --method sym_qsp poly_sign
pyqsp --plot --seqargs=19,0.25 --method sym_qsp poly_linear_amp
pyqsp --plot --seqargs=68,20 --method sym_qsp poly_phase
pyqsp --plot --seqargs=20,0.6,15 --method sym_qsp relu
pyqsp --plot --seqargs=3,0.1 --method sym_qsp invert
pyqsp --plot --func "np.sign(x)" --polydeg 151 --scale 0.5 sym_qsp_func
pyqsp --plot --func "np.sin(10*x)" --polydeg 31 sym_qsp_func
pyqsp --plot --func "np.sign(x - 0.5) + np.sign(-1*x - 0.5)" --polydeg 151 --scale 0.9 sym_qsp_func
pyqsp --plot --tolerance=0.01 --seqargs 3 invert
# Note older examples using the 'laurent' method.
pyqsp --plot --tolerance=0.1 --seqargs 2 invert
pyqsp --plot-npts=4000 --plot-positive-only --plot-magnitude --plot --seqargs=1000,1.0e-20 --seqname fpsearch angles
pyqsp --plot-npts=100 --plot-magnitude --plot --seqargs=23 --seqname erf_step angles
pyqsp --plot-npts=100 --plot-positive-only --plot --seqargs=23 --seqname erf_step angles
pyqsp --plot-real-only --plot --polyargs=20,20 --polyname poly_thresh poly
pyqsp --plot-positive-only --plot --polyargs=19,10 --plot-real-only --polyname poly_sign poly
pyqsp --plot-positive-only --plot-real-only --plot --polyargs 20,3.5 --polyname gibbs poly
pyqsp --plot-positive-only --plot-real-only --plot --polyargs 20,0.2,0.9 --polyname efilter poly
# Note older examples using the deprecated 'tf' method.
pyqsp --plot-positive-only --plot --polyargs=19,10 --plot-real-only --polyname poly_sign --method tf poly
pyqsp --plot --func "np.cos(3*x)" --polydeg 6 polyfunc
pyqsp --plot --func "np.cos(3*x)" --polydeg 6 --plot-qsp-model polyfunc
Expand Down Expand Up @@ -132,15 +138,21 @@ def float_list(value):
help="for tf method, degree of polynomial to use in generating approximation of specified function (see --func)",
type=int)
parser.add_argument(
"--tau",
help="time value for Hamiltonian simulation (hamsim command)",
type=float,
default=100)
parser.add_argument(
"--epsilon",
help="parameter for polynomial approximation to 1/a, giving bound on error",
"--scale",
help="Within 'sym_qsp' method, specifies a float to which the extreme point of the approximating polynomial is rescaled in absolute value. For highly oscillatory functions, try decreasing this parameter toward zero.",
type=float,
default=0.3)
default=0.9)
# The arguments below have been deprecated and subsumed into --seqargs.
# parser.add_argument(
# "--tau",
# help="time value for Hamiltonian simulation (hamsim command)",
# type=float,
# default=100)
# parser.add_argument(
# "--epsilon",
# help="parameter for polynomial approximation to 1/a, giving bound on error",
# type=float,
# default=0.3)
parser.add_argument(
"--seqname",
help="name of QSP phase angle sequence to generate using the 'angles' command, e.g. fpsearch",
Expand Down Expand Up @@ -207,7 +219,7 @@ def float_list(value):
default=0.1)
parser.add_argument(
"--method",
help="method to use for qsp phase angle generation, either 'laurent' (default) or 'tf' (for tensorflow + keras)",
help="method to use for qsp phase angle generation, either 'laurent' (default), 'sym_qsp' for iterative methods involving symmetric QSP, or 'tf' (for tensorflow + keras)",
type=str,
default='laurent')
parser.add_argument(
Expand Down Expand Up @@ -622,19 +634,29 @@ def float_list(value):
raise ValueError(
f"Invalid function specifciation, failed to evaluate at x=0.5, err={err}")

# Note that the polynomial is renormalized once according to its maximum magnitude (both up and down), and then again according to specified scale in a multiplicative way. We ought to be able to specify this, or at least return the metadata.

# TODO: new addition for manual rescaling within sym_qsp method.
if args.scale:
if np.abs(args.scale >= 1) or (args.scale <= 0):
raise ValueError(
f"Invalid scale specification (scale = {args.scale}); must be positive and less than 1.")
else:
max_scale = args.scale
else:
max_scale = 0.9

# Generate anonymous function evaluated at x.
func = lambda x: eval(args.func, globals(), {'x': x})

# Note that the polynomial is renormalized once according to its maximum magnitude (both up and down), and then again according to specified scale in a multiplicative way. We ought to be able to specify this, or at least return the metadata.

# Note that QuantumSignalProcessingPhases will determine if the parity is not definite, allowing us to use below method raw.
base_poly = PolyTaylorSeries()
poly, scale = base_poly.taylor_series(
func=func,
degree=args.polydeg,
ensure_bounded=True,
return_scale=True,
max_scale=0.9,
max_scale=max_scale,
chebyshev_basis=is_sym_qsp,
cheb_samples=2*args.polydeg) # Set larger than polydeg to prevent aliasing.

Expand All @@ -646,11 +668,14 @@ def float_list(value):
poly,
**qspp_args)

# Given polynomial approximation scale factor, generate new possibly rescaled target function for plotting purposes.
plot_func = lambda x: scale * eval(args.func, globals(), {'x': x})

# Finally, if plotting called for, plot while passing 'sym_qsp' flag.
if args.plot:
response.PlotQSPResponse(
phiset,
target=poly,
target=plot_func, # Plot against ideal function, not poly approx
title=args.func, # Support to show function plotted.
signal_operator="Wx",
**plot_args)
Expand Down
12 changes: 12 additions & 0 deletions pyqsp/poly.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import scipy.special
from scipy.interpolate import approximate_taylor_polynomial

import matplotlib.pyplot as plt

# -----------------------------------------------------------------------------

class StringPolynomial:
Expand Down Expand Up @@ -258,6 +260,16 @@ def generate(
print("[PolyOneOverX] bounding to 0.5")
g = scale * g

# # Internal plotting for debugging purposes.
# npts = 500
# adat = np.linspace(-1, 1, npts)
# plt.plot(adat, g(adat))
# plt.ylim([-1.1, 1.1])
# ax = plt.gca()
# ax.spines['top'].set_visible(False)
# ax.spines['right'].set_visible(False)
# plt.show()

if return_coef:
if chebyshev_basis:
pcoefs = g.coef
Expand Down
Loading

0 comments on commit 37040dd

Please sign in to comment.