From a1e13d46d3226e08892f72909ac693ba83ceb925 Mon Sep 17 00:00:00 2001 From: Anurudh Peduri Date: Fri, 13 Sep 2024 22:37:10 +0200 Subject: [PATCH] adjoints, fix guiding state overlap --- .../bloqs/max_k_xor_sat/arithmetic/equals.py | 3 ++ .../guided_hamiltonian/walk_operator.py | 3 ++ .../max_k_xor_sat/kikuchi_adjacency_list.py | 6 ++++ .../max_k_xor_sat/kikuchi_block_encoding.py | 3 ++ .../bloqs/max_k_xor_sat/planted_noisy_kxor.py | 23 ++++++++++++-- .../max_k_xor_sat/planted_noisy_kxor_test.py | 31 +++++++++++++------ qualtran/bloqs/mcmt/multi_control_pauli.py | 6 ++++ 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/qualtran/bloqs/max_k_xor_sat/arithmetic/equals.py b/qualtran/bloqs/max_k_xor_sat/arithmetic/equals.py index b34ce0943..9d15aa560 100644 --- a/qualtran/bloqs/max_k_xor_sat/arithmetic/equals.py +++ b/qualtran/bloqs/max_k_xor_sat/arithmetic/equals.py @@ -59,3 +59,6 @@ def build_composite_bloq( def build_call_graph(self, ssa: 'SympySymbolAllocator') -> BloqCountDictT: return {Xor(QAny(self.bitsize)): 2, MultiControlX(cvs=HasLength(self.bitsize)): 1} + + def adjoint(self) -> 'Bloq': + return self diff --git a/qualtran/bloqs/max_k_xor_sat/guided_hamiltonian/walk_operator.py b/qualtran/bloqs/max_k_xor_sat/guided_hamiltonian/walk_operator.py index 512227f49..3d7b9e49a 100644 --- a/qualtran/bloqs/max_k_xor_sat/guided_hamiltonian/walk_operator.py +++ b/qualtran/bloqs/max_k_xor_sat/guided_hamiltonian/walk_operator.py @@ -89,3 +89,6 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> dict[str def build_call_graph(self, ssa: 'SympySymbolAllocator') -> BloqCountDictT: return {self.block_encoding: 1, self.reflect: 1} + + def __str__(self): + return f'Walk[{self.block_encoding}]' diff --git a/qualtran/bloqs/max_k_xor_sat/kikuchi_adjacency_list.py b/qualtran/bloqs/max_k_xor_sat/kikuchi_adjacency_list.py index 41bc5b325..dd2ec76f6 100644 --- a/qualtran/bloqs/max_k_xor_sat/kikuchi_adjacency_list.py +++ b/qualtran/bloqs/max_k_xor_sat/kikuchi_adjacency_list.py @@ -99,6 +99,9 @@ def signature(self) -> 'Signature': def index_bitsize(self) -> SymbolicInt: return self.ell * self.inst.index_bitsize + def adjoint(self) -> 'ColumnOfKthNonZeroEntry': + return self + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': m = self.inst.num_unique_constraints ell, k = self.ell, self.inst.k @@ -206,6 +209,9 @@ def signature(self) -> 'Signature': def index_bitsize(self) -> SymbolicInt: return self.ell * self.inst.index_bitsize + def adjoint(self) -> 'IndexOfNonZeroColumn': + return self + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': m = self.inst.num_unique_constraints ell, k = self.ell, self.inst.k diff --git a/qualtran/bloqs/max_k_xor_sat/kikuchi_block_encoding.py b/qualtran/bloqs/max_k_xor_sat/kikuchi_block_encoding.py index 7028f9157..79d7b45a9 100644 --- a/qualtran/bloqs/max_k_xor_sat/kikuchi_block_encoding.py +++ b/qualtran/bloqs/max_k_xor_sat/kikuchi_block_encoding.py @@ -205,6 +205,9 @@ def signal_state(self) -> BlackBoxPrepare: def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> dict[str, 'SoquetT']: return bb.add_d(self._sparse_matrix_encoding, **soqs) + def __str__(self): + return 'B[K_l]' + @bloq_example def _kikuchi_matrix() -> KikuchiHamiltonian: diff --git a/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor.py b/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor.py index 37a36d71c..ea1e245ca 100644 --- a/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor.py +++ b/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor.py @@ -17,7 +17,7 @@ import numpy as np import scipy import sympy -from attrs import frozen +from attrs import field, frozen from qualtran import Bloq, bloq_example, BloqBuilder, BloqDocSpec, Signature, SoquetT from qualtran.bloqs.state_preparation.black_box_prepare import BlackBoxPrepare @@ -219,6 +219,7 @@ class PlantedNoisyKXOR(Bloq): inst_solve: KXorInstance ell: SymbolicInt rho: SymbolicFloat + _guiding_state_overlap: Optional[SymbolicFloat] = field(kw_only=True, default=None) def __attrs_post_init__(self): k = self.inst_guide.k @@ -242,6 +243,7 @@ def from_inst( *, zeta: Optional[SymbolicFloat], rng: np.random.Generator, + guiding_state_overlap: Optional[SymbolicFloat] = None, ): (use_for_guide,) = np.nonzero(np.atleast_1d(rng.random(inst.m) < zeta)) inst_guide = inst.subset(tuple(use_for_guide)) @@ -254,7 +256,13 @@ def from_inst( (rest,) = np.nonzero(mask) inst_solve = inst.subset(tuple(rest)) - return cls(inst_guide=inst_guide, inst_solve=inst_solve, ell=ell, rho=rho) + return cls( + inst_guide=inst_guide, + inst_solve=inst_solve, + ell=ell, + rho=rho, + guiding_state_overlap=guiding_state_overlap, + ) @cached_property def guiding_state_and_coefficient(self) -> tuple[PrepareOracle, SymbolicFloat]: @@ -286,10 +294,17 @@ def guiding_state_overlap_guarantee(self) -> GuidingStateOverlapTheorem: n=n, k=k, ell=self.ell, m_hat=m_hat, zeta=zeta, nu=1 / ln(n), eps=0.005, rho=self.rho ) + @cached_property + def guiding_state_overlap(self) -> SymbolicFloat: + if self._guiding_state_overlap is not None: + return self.guiding_state_overlap + _, guiding_state_good_coeff = self.guiding_state_and_coefficient + return guiding_state_good_coeff + @cached_property def overlap(self) -> SymbolicFloat: # guiding state - _, guiding_state_good_coeff = self.guiding_state_and_coefficient + guiding_state_good_coeff = self.guiding_state_overlap # overlap of |\Gamma(A)> with the threshold eigenspace overlap_good_eigen = self.guiding_state_overlap_guarantee.overlap_probability**0.5 @@ -330,6 +345,8 @@ def guided_hamiltonian_bloq(self) -> GuidedHamiltonian: kappa = 0.99 * self.rho eps = 0.005 alpha = 1 - (kappa + eps) / eigenvalue_threshold + if not is_symbolic(alpha): + assert alpha > 0 guiding_state, _ = self.guiding_state_and_coefficient diff --git a/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor_test.py b/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor_test.py index 9bf9acc84..85b164294 100644 --- a/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor_test.py +++ b/qualtran/bloqs/max_k_xor_sat/planted_noisy_kxor_test.py @@ -64,15 +64,7 @@ def test_call_graph_symb(): show_call_graph(g) -def example_random_instance() -> PlantedNoisyKXOR: - # configure parameters here - k = 4 - n, m = 100, 1000 - rho = 0.8 - seed = 120 - - c = 2 # Kikuchi param: ell = c * k - +def example_random_instance(*, k=4, n=100, m=1000, c=2, rho=0.8, seed=120) -> PlantedNoisyKXOR: # generate instance rng = np.random.default_rng(seed) ell = c * k @@ -97,3 +89,24 @@ def test_gate_cost(): print(t_cost) print(t_cost / big_O_expected) print(big_O_expected) + print(t_cost / big_O_expected * bloq.guiding_state_overlap) + print(1 / bloq.guiding_state_overlap) + print(1 / bloq.guiding_state_overlap_guarantee.overlap_probability**0.5) + + +@pytest.mark.parametrize("n", [40, 50, 60, 70, 80, 90, 100]) +@pytest.mark.parametrize("k", [4, 8]) +@pytest.mark.parametrize("c", [2, 3, 4]) +def test_more_costs(n, k, c): + bloq = example_random_instance(k=k, c=c, n=n, m=n, seed=142) + cost = get_cost_value(bloq, QECGatesCost()) + print(cost) + + +@pytest.mark.parametrize("n", [10**4, 10**5]) +def test_large(n): + k = 4 + c = 32 // 4 + bloq = example_random_instance(k=k, c=c, n=n, m=n * 10, seed=142) + cost = get_cost_value(bloq, QECGatesCost()) + print(cost) diff --git a/qualtran/bloqs/mcmt/multi_control_pauli.py b/qualtran/bloqs/mcmt/multi_control_pauli.py index dbbeaa227..0d5c5f98c 100644 --- a/qualtran/bloqs/mcmt/multi_control_pauli.py +++ b/qualtran/bloqs/mcmt/multi_control_pauli.py @@ -191,6 +191,9 @@ class MultiControlX(MultiControlPauli): def _X(self): return cirq.X + def adjoint(self) -> 'Bloq': + return self + @frozen class MultiControlZ(MultiControlPauli): @@ -203,3 +206,6 @@ class MultiControlZ(MultiControlPauli): @target_gate.default def _Z(self): return cirq.Z + + def adjoint(self) -> 'Bloq': + return self