From 0b17665bbe0aab0a3ce27819d2a21a197d460c76 Mon Sep 17 00:00:00 2001 From: Nobuyuki Yoshioka Date: Wed, 13 Aug 2025 16:00:38 +0900 Subject: [PATCH 1/4] allow theta and epsilon to be float --- pygridsynth/gridsynth.py | 174 ++++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 68 deletions(-) diff --git a/pygridsynth/gridsynth.py b/pygridsynth/gridsynth.py index 8ea87d5..df1d95e 100644 --- a/pygridsynth/gridsynth.py +++ b/pygridsynth/gridsynth.py @@ -85,10 +85,15 @@ def generate_target_Rz(theta): def error(theta, gates): - Rz = generate_target_Rz(theta) - U = DOmegaUnitary.from_gates(gates).to_complex_matrix - E = U - Rz - return sqrt(mpmath.fabs(E[0, 0] * E[1, 1] - E[0, 1] * E[1, 0])) + tcount = gates.count("T") + epsilon = 2 ** (-tcount//3) + dps = _dps_for_epsilon(epsilon) + with mp_dps(dps): + + Rz = generate_target_Rz(mpmath.mpmathify(f"{theta}")) + U = DOmegaUnitary.from_gates(gates).to_complex_matrix + E = U - Rz + return sqrt(mpmath.fabs(E[0, 0] * E[1, 1] - E[0, 1] * E[1, 0])) def check(theta, gates): @@ -106,77 +111,110 @@ def check(theta, gates): print(f"{e=}") -def gridsynth(theta, epsilon, +def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, diophantine_timeout=200, factoring_timeout=50, verbose=False, measure_time=False, show_graph=False): - epsilon_region = EpsilonRegion(theta, epsilon) - unit_disk = UnitDisk() - k = 0 - - if measure_time: - start = time.time() - transformed = to_upright_set_pair(epsilon_region, unit_disk, - verbose=verbose, show_graph=show_graph) - if measure_time: - print(f"to_upright_set_pair: {time.time() - start} s") - if verbose: - print("------------------") - - time_of_solve_TDGP = 0 - time_of_diophantine_dyadic = 0 - while True: - if measure_time: - start = time.time() - sol = solve_TDGP(epsilon_region, unit_disk, *transformed, k, - verbose=verbose, show_graph=show_graph) + dps = _dps_for_epsilon(epsilon) + if not isinstance(epsilon, mpmath.mpf) or not isinstance(theta, mpmath.mpf): + + mpmath.mp.dps = dps + epsilon = mpmath.mpmathify(f"{epsilon}") + theta = mpmath.mpmathify(f"{theta}") + + with mp_dps(dps): + epsilon_region = EpsilonRegion(theta, epsilon) + unit_disk = UnitDisk() + k = 0 + + if measure_time: - time_of_solve_TDGP += time.time() - start start = time.time() - - for z in sol: - if (z * z.conj).residue == 0: - continue - xi = 1 - DRootTwo.fromDOmega(z.conj * z) - w = diophantine_dyadic(xi, - diophantine_timeout=diophantine_timeout, - factoring_timeout=factoring_timeout) - if w != NO_SOLUTION: - z = z.reduce_denomexp() - w = w.reduce_denomexp() - if z.k > w.k: - w = w.renew_denomexp(z.k) - elif z.k < w.k: - z = z.renew_denomexp(w.k) - if (z + w).reduce_denomexp().k < z.k: - u_approx = DOmegaUnitary(z, w, 0) - else: - u_approx = DOmegaUnitary(z, w.mul_by_omega(), 0) - if measure_time: - time_of_diophantine_dyadic += time.time() - start - print(f"time of solve_TDGP: {time_of_solve_TDGP * 1000} ms") - print(f"time of diophantine_dyadic: {time_of_diophantine_dyadic * 1000} ms") - if verbose: - print(f"{z=}, {w=}") - print("------------------") - return u_approx + transformed = to_upright_set_pair(epsilon_region, unit_disk, + verbose=verbose, show_graph=show_graph) if measure_time: - time_of_diophantine_dyadic += time.time() - start - k += 1 + print(f"to_upright_set_pair: {time.time() - start} s") + if verbose: + print("------------------") + + time_of_solve_TDGP = 0 + time_of_diophantine_dyadic = 0 + while True: + if measure_time: + start = time.time() + sol = solve_TDGP(epsilon_region, unit_disk, *transformed, k, + verbose=verbose, show_graph=show_graph) + if measure_time: + time_of_solve_TDGP += time.time() - start + start = time.time() + + for z in sol: + if (z * z.conj).residue == 0: + continue + xi = 1 - DRootTwo.fromDOmega(z.conj * z) + w = diophantine_dyadic(xi, + diophantine_timeout=diophantine_timeout, + factoring_timeout=factoring_timeout) + if w != NO_SOLUTION: + z = z.reduce_denomexp() + w = w.reduce_denomexp() + if z.k > w.k: + w = w.renew_denomexp(z.k) + elif z.k < w.k: + z = z.renew_denomexp(w.k) + if (z + w).reduce_denomexp().k < z.k: + u_approx = DOmegaUnitary(z, w, 0) + else: + u_approx = DOmegaUnitary(z, w.mul_by_omega(), 0) + if measure_time: + time_of_diophantine_dyadic += time.time() - start + print(f"time of solve_TDGP: {time_of_solve_TDGP * 1000} ms") + print(f"time of diophantine_dyadic: {time_of_diophantine_dyadic * 1000} ms") + if verbose: + print(f"{z=}, {w=}") + print("------------------") + return u_approx + if measure_time: + time_of_diophantine_dyadic += time.time() - start + k += 1 def gridsynth_gates(theta, epsilon, diophantine_timeout=200, factoring_timeout=50, verbose=False, measure_time=False, show_graph=False): - if measure_time: - start_total = time.time() - u_approx = gridsynth(theta=theta, epsilon=epsilon, - diophantine_timeout=diophantine_timeout, - factoring_timeout=factoring_timeout, - verbose=verbose, measure_time=measure_time, show_graph=show_graph) - if measure_time: - start = time.time() - gates = decompose_domega_unitary(u_approx) - if measure_time: - print(f"time of decompose_domega_unitary: {(time.time() - start) * 1000} ms") - print(f"total time: {(time.time() - start_total) * 1000} ms") - return gates + dps = _dps_for_epsilon(epsilon) + with mp_dps(dps): + theta = mpmath.mpmathify(f"{theta}") + epsilon = mpmath.mpmathify(f"{epsilon}") + + if measure_time: + start_total = time.time() + u_approx = gridsynth(theta=theta, epsilon=epsilon, + diophantine_timeout=diophantine_timeout, + factoring_timeout=factoring_timeout, + verbose=verbose, measure_time=measure_time, show_graph=show_graph) + if measure_time: + start = time.time() + gates = decompose_domega_unitary(u_approx) + if measure_time: + print(f"time of decompose_domega_unitary: {(time.time() - start) * 1000} ms") + print(f"total time: {(time.time() - start_total) * 1000} ms") + return gates + +def _dps_for_epsilon(eps_float) -> int: + old = mpmath.mp.dps + try: + e = mpmath.mpmathify(f"{eps_float}") + k = -mpmath.log10(e) + return 15 + 2.5*int(mpmath.ceil(k)) + finally: + mpmath.mp.dps = old + +class mp_dps: + def __init__(self, dps): + self._old = None + self._dps = dps + def __enter__(self): + self._old = mpmath.mp.dps + mpmath.mp.dps = self._dps + def __exit__(self, *exc): + mpmath.mp.dps = self._old \ No newline at end of file From c7cf783969163c051f15ad7dbc68805e9fbddca5 Mon Sep 17 00:00:00 2001 From: Nobuyuki Yoshioka Date: Wed, 13 Aug 2025 16:22:39 +0900 Subject: [PATCH 2/4] add a function to get the synthesized unitary --- pygridsynth/gridsynth.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pygridsynth/gridsynth.py b/pygridsynth/gridsynth.py index df1d95e..7fcc149 100644 --- a/pygridsynth/gridsynth.py +++ b/pygridsynth/gridsynth.py @@ -110,9 +110,16 @@ def check(theta, gates): # print(f"{U=}") print(f"{e=}") +def synthesized_unitary(gates): + tcount = gates.count("T") + epsilon = 2 ** (-tcount//3) + dps = _dps_for_epsilon(epsilon) + with mp_dps(dps): + return DOmegaUnitary.from_gates(gates).to_complex_matrix + def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, - diophantine_timeout=200, factoring_timeout=50, + diophantine_timeout=1, factoring_timeout=1, verbose=False, measure_time=False, show_graph=False): dps = _dps_for_epsilon(epsilon) if not isinstance(epsilon, mpmath.mpf) or not isinstance(theta, mpmath.mpf): @@ -179,7 +186,7 @@ def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, def gridsynth_gates(theta, epsilon, - diophantine_timeout=200, factoring_timeout=50, + diophantine_timeout=1, factoring_timeout=1, verbose=False, measure_time=False, show_graph=False): dps = _dps_for_epsilon(epsilon) with mp_dps(dps): @@ -205,7 +212,7 @@ def _dps_for_epsilon(eps_float) -> int: try: e = mpmath.mpmathify(f"{eps_float}") k = -mpmath.log10(e) - return 15 + 2.5*int(mpmath.ceil(k)) + return 15 + 2.5*int(mpmath.ceil(k)) # used in newsynth finally: mpmath.mp.dps = old From ddc37bd39d1b8405c28fc50227533474ba3a429b Mon Sep 17 00:00:00 2001 From: Nobuyuki Yoshioka Date: Wed, 13 Aug 2025 16:38:33 +0900 Subject: [PATCH 3/4] change default timeout for speed up --- pygridsynth/gridsynth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygridsynth/gridsynth.py b/pygridsynth/gridsynth.py index 7fcc149..4529c2e 100644 --- a/pygridsynth/gridsynth.py +++ b/pygridsynth/gridsynth.py @@ -110,7 +110,7 @@ def check(theta, gates): # print(f"{U=}") print(f"{e=}") -def synthesized_unitary(gates): +def get_synthesized_unitary(gates): tcount = gates.count("T") epsilon = 2 ** (-tcount//3) dps = _dps_for_epsilon(epsilon) @@ -119,7 +119,7 @@ def synthesized_unitary(gates): def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, - diophantine_timeout=1, factoring_timeout=1, + diophantine_timeout=1.0, factoring_timeout=1.0, verbose=False, measure_time=False, show_graph=False): dps = _dps_for_epsilon(epsilon) if not isinstance(epsilon, mpmath.mpf) or not isinstance(theta, mpmath.mpf): @@ -186,7 +186,7 @@ def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, def gridsynth_gates(theta, epsilon, - diophantine_timeout=1, factoring_timeout=1, + diophantine_timeout= 1.0, factoring_timeout= 1.0, verbose=False, measure_time=False, show_graph=False): dps = _dps_for_epsilon(epsilon) with mp_dps(dps): From b31594eea29a291f74429743291c34d88d8d0669 Mon Sep 17 00:00:00 2001 From: shun0923 Date: Fri, 15 Aug 2025 09:26:44 +0900 Subject: [PATCH 4/4] Add warning for float input and use mpmath.workdps --- pygridsynth/__main__.py | 8 +--- pygridsynth/gridsynth.py | 85 +++++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/pygridsynth/__main__.py b/pygridsynth/__main__.py index afb3b6b..1f6ae4a 100644 --- a/pygridsynth/__main__.py +++ b/pygridsynth/__main__.py @@ -9,7 +9,7 @@ def main(): parser.add_argument('theta', type=str) parser.add_argument('epsilon', type=str) - parser.add_argument('--dps', type=int, default=128) + parser.add_argument('--dps', type=int, default=None) parser.add_argument('--dtimeout', '-dt', type=int, default=200) parser.add_argument('--ftimeout', '-ft', type=int, default=50) parser.add_argument('--verbose', '-v', action='store_true') @@ -17,12 +17,8 @@ def main(): parser.add_argument('--showgraph', '-g', action='store_true') args = parser.parse_args() - mpmath.mp.dps = args.dps - mpmath.mp.pretty = True - theta = mpmath.mpmathify(args.theta) - epsilon = mpmath.mpmathify(args.epsilon) - gates = gridsynth_gates(theta=theta, epsilon=epsilon, + gates = gridsynth_gates(theta=args.theta, epsilon=args.epsilon, dps=args.dps, factoring_timeout=args.ftimeout, diophantine_timeout=args.dtimeout, verbose=args.verbose, measure_time=args.time, diff --git a/pygridsynth/gridsynth.py b/pygridsynth/gridsynth.py index 4529c2e..608103c 100644 --- a/pygridsynth/gridsynth.py +++ b/pygridsynth/gridsynth.py @@ -1,5 +1,6 @@ import mpmath import time +import warnings from .mymath import sqrt, solve_quadratic from .ring import DRootTwo @@ -88,8 +89,7 @@ def error(theta, gates): tcount = gates.count("T") epsilon = 2 ** (-tcount//3) dps = _dps_for_epsilon(epsilon) - with mp_dps(dps): - + with mpmath.workdps(dps): Rz = generate_target_Rz(mpmath.mpmathify(f"{theta}")) U = DOmegaUnitary.from_gates(gates).to_complex_matrix E = U - Rz @@ -114,21 +114,34 @@ def get_synthesized_unitary(gates): tcount = gates.count("T") epsilon = 2 ** (-tcount//3) dps = _dps_for_epsilon(epsilon) - with mp_dps(dps): + with mpmath.workdps(dps): return DOmegaUnitary.from_gates(gates).to_complex_matrix -def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, +def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, dps=None, diophantine_timeout=1.0, factoring_timeout=1.0, verbose=False, measure_time=False, show_graph=False): - dps = _dps_for_epsilon(epsilon) - if not isinstance(epsilon, mpmath.mpf) or not isinstance(theta, mpmath.mpf): - - mpmath.mp.dps = dps - epsilon = mpmath.mpmathify(f"{epsilon}") - theta = mpmath.mpmathify(f"{theta}") + if isinstance(theta, float): + warnings.warn( + f"pygridsynth is synthesizing the angle {theta}, please verify if this is the intended value. " + "Float values may introduce unintended precision errors. Consider using mpmath.mpf for exact precision.", + UserWarning, + stacklevel=2 + ) + if isinstance(epsilon, float): + warnings.warn( + f"pygridsynth is synthesizing the epsilon {epsilon}, please verify if this is the intended value. " + "Float values may introduce unintended precision errors. Consider using mpmath.mpf for exact precision.", + UserWarning, + stacklevel=2 + ) + + if dps is None: + dps = _dps_for_epsilon(epsilon) + with mpmath.workdps(dps): + theta = mpmath.mpmathify(theta) + epsilon = mpmath.mpmathify(epsilon) - with mp_dps(dps): epsilon_region = EpsilonRegion(theta, epsilon) unit_disk = UnitDisk() k = 0 @@ -185,17 +198,33 @@ def gridsynth(theta:mpmath.mpf, epsilon:mpmath.mpf, k += 1 -def gridsynth_gates(theta, epsilon, +def gridsynth_gates(theta, epsilon, dps=None, diophantine_timeout= 1.0, factoring_timeout= 1.0, verbose=False, measure_time=False, show_graph=False): - dps = _dps_for_epsilon(epsilon) - with mp_dps(dps): - theta = mpmath.mpmathify(f"{theta}") - epsilon = mpmath.mpmathify(f"{epsilon}") + if isinstance(theta, float): + warnings.warn( + f"pygridsynth is synthesizing the angle {theta}, please verify if this is the intended value. " + "Float values may introduce unintended precision errors. Consider using mpmath.mpf for exact precision.", + UserWarning, + stacklevel=2 + ) + if isinstance(epsilon, float): + warnings.warn( + f"pygridsynth is synthesizing the epsilon {epsilon}, please verify if this is the intended value. " + "Float values may introduce unintended precision errors. Consider using mpmath.mpf for exact precision.", + UserWarning, + stacklevel=2 + ) + + if dps is None: + dps = _dps_for_epsilon(epsilon) + with mpmath.workdps(dps): + theta = mpmath.mpmathify(theta) + epsilon = mpmath.mpmathify(epsilon) if measure_time: start_total = time.time() - u_approx = gridsynth(theta=theta, epsilon=epsilon, + u_approx = gridsynth(theta=theta, epsilon=epsilon, dps=dps, diophantine_timeout=diophantine_timeout, factoring_timeout=factoring_timeout, verbose=verbose, measure_time=measure_time, show_graph=show_graph) @@ -207,21 +236,7 @@ def gridsynth_gates(theta, epsilon, print(f"total time: {(time.time() - start_total) * 1000} ms") return gates -def _dps_for_epsilon(eps_float) -> int: - old = mpmath.mp.dps - try: - e = mpmath.mpmathify(f"{eps_float}") - k = -mpmath.log10(e) - return 15 + 2.5*int(mpmath.ceil(k)) # used in newsynth - finally: - mpmath.mp.dps = old - -class mp_dps: - def __init__(self, dps): - self._old = None - self._dps = dps - def __enter__(self): - self._old = mpmath.mp.dps - mpmath.mp.dps = self._dps - def __exit__(self, *exc): - mpmath.mp.dps = self._old \ No newline at end of file +def _dps_for_epsilon(epsilon) -> int: + e = mpmath.mpmathify(epsilon) + k = -mpmath.log10(e) + return 15 + 2.5*int(mpmath.ceil(k)) # used in newsynth