Skip to content

Commit 371613e

Browse files
authored
Symbolic Shaped (#867)
* support `Shaped` for symbolic analysis fix expression for Jacobi-Anger degree add test using 1d ising walk op, fix call graph fix test fix hamsim nb `slen`, docstrings, generalize `is_symbolic` (to remove dep. on cirq) test shape with symbolic entries docstring refs: remove extra indent skip serialize test for symbolic hamsim * remove `_is_parameterized_` * unittest gqsp symbolic call graph * lint * fix type hint
1 parent cbc3727 commit 371613e

10 files changed

Lines changed: 241 additions & 64 deletions

qualtran/bloqs/basic_gates/su2_rotation.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,13 @@ def add_my_tensors(
117117
)
118118

119119
def _unitary_(self):
120-
if self._is_parameterized_():
120+
if self.is_symbolic():
121121
return None
122122
return self.rotation_matrix
123123

124124
def build_composite_bloq(self, bb: 'BloqBuilder', q: 'SoquetT') -> Dict[str, 'SoquetT']:
125-
pi = sympy.pi if self._is_parameterized_() else np.pi
126-
exp = sympy.exp if self._is_parameterized_() else np.exp
125+
pi = sympy.pi if self.is_symbolic() else np.pi
126+
exp = sympy.exp if self.is_symbolic() else np.exp
127127

128128
bb.add(GlobalPhase(coefficient=-exp(1j * self.global_shift), eps=self.eps / 4))
129129
q = bb.add(ZPowGate(exponent=1 - self.lambd / pi, global_shift=-1, eps=self.eps / 4), q=q)
@@ -151,7 +151,7 @@ def wire_symbol(self, soq: 'Soquet') -> 'WireSymbol':
151151
def _t_complexity_(self) -> TComplexity:
152152
return TComplexity(rotations=3)
153153

154-
def _is_parameterized_(self) -> bool:
154+
def is_symbolic(self) -> bool:
155155
return is_symbolic(self.theta, self.phi, self.lambd, self.global_shift)
156156

157157
@classmethod

qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.ipynb

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"This therefore block-encodes $e^{-iHt}$ in the block where the signal qubit and selection registers are all $|0\\rangle$.\n",
8181
"\n",
8282
"#### References\n",
83-
" - [Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501). Motlagh and Wiebe. (2023). Theorem 7, Corollary 8. \n",
83+
" - [Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501). Motlagh and Wiebe. (2023). Theorem 7, Corollary 8. \n",
8484
"\n",
8585
"#### Parameters\n",
8686
" - `walk_operator`: qubitization walk operator of $H$ constructed from SELECT and PREPARE oracles.\n",
@@ -128,6 +128,26 @@
128128
")"
129129
]
130130
},
131+
{
132+
"cell_type": "code",
133+
"execution_count": null,
134+
"id": "11baa846",
135+
"metadata": {
136+
"cq.autogen": "HamiltonianSimulationByGQSP.symbolic_hamsim_by_gqsp"
137+
},
138+
"outputs": [],
139+
"source": [
140+
"import sympy\n",
141+
"\n",
142+
"from qualtran.bloqs.hubbard_model import get_walk_operator_for_hubbard_model\n",
143+
"\n",
144+
"walk_op = get_walk_operator_for_hubbard_model(2, 2, 1, 1)\n",
145+
"t, alpha, inv_eps = sympy.symbols(\"t alpha N\")\n",
146+
"symbolic_hamsim_by_gqsp = HamiltonianSimulationByGQSP(\n",
147+
" walk_op, t=t, alpha=alpha, precision=1 / inv_eps\n",
148+
")"
149+
]
150+
},
131151
{
132152
"cell_type": "markdown",
133153
"id": "a6e29295",
@@ -148,8 +168,8 @@
148168
"outputs": [],
149169
"source": [
150170
"from qualtran.drawing import show_bloqs\n",
151-
"show_bloqs([hubbard_time_evolution_by_gqsp],\n",
152-
" ['`hubbard_time_evolution_by_gqsp`'])"
171+
"show_bloqs([hubbard_time_evolution_by_gqsp, symbolic_hamsim_by_gqsp],\n",
172+
" ['`hubbard_time_evolution_by_gqsp`', '`symbolic_hamsim_by_gqsp`'])"
153173
]
154174
},
155175
{
@@ -171,21 +191,42 @@
171191
},
172192
"outputs": [],
173193
"source": [
174-
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
175-
"hubbard_time_evolution_by_gqsp_g, hubbard_time_evolution_by_gqsp_sigma = hubbard_time_evolution_by_gqsp.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
194+
"from qualtran.resource_counting.generalizers import generalize_rotation_angle, ignore_split_join, ignore_alloc_free\n",
195+
"hubbard_time_evolution_by_gqsp_g, hubbard_time_evolution_by_gqsp_sigma = hubbard_time_evolution_by_gqsp.call_graph(generalizer=[ignore_split_join, ignore_alloc_free, generalize_rotation_angle])\n",
176196
"show_call_graph(hubbard_time_evolution_by_gqsp_g)\n",
177197
"show_counts_sigma(hubbard_time_evolution_by_gqsp_sigma)"
178198
]
199+
},
200+
{
201+
"cell_type": "code",
202+
"execution_count": null,
203+
"id": "7045d204b9fcd6f1",
204+
"metadata": {},
205+
"outputs": [],
206+
"source": [
207+
"from qualtran.resource_counting.generalizers import generalize_rotation_angle, ignore_split_join, ignore_alloc_free\n",
208+
"_, symbolic_hamsim_by_gqsp_sigma = symbolic_hamsim_by_gqsp.call_graph(max_depth=2, generalizer=[ignore_split_join, ignore_alloc_free, generalize_rotation_angle])\n",
209+
"show_counts_sigma(symbolic_hamsim_by_gqsp_sigma)"
210+
]
179211
}
180212
],
181213
"metadata": {
182214
"kernelspec": {
183-
"display_name": "Python 3",
215+
"display_name": "Python 3 (ipykernel)",
184216
"language": "python",
185217
"name": "python3"
186218
},
187219
"language_info": {
188-
"name": "python"
220+
"codemirror_mode": {
221+
"name": "ipython",
222+
"version": 3
223+
},
224+
"file_extension": ".py",
225+
"mimetype": "text/x-python",
226+
"name": "python",
227+
"nbconvert_exporter": "python",
228+
"pygments_lexer": "ipython3",
229+
"version": "3.11.8"
189230
}
190231
},
191232
"nbformat": 4,

qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp.py

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
from functools import cached_property
15-
from typing import Dict, Set, Tuple, TYPE_CHECKING
15+
from typing import Dict, Tuple, TYPE_CHECKING, Union
1616

1717
import numpy as np
1818
from attrs import field, frozen
1919
from numpy.typing import NDArray
2020

21-
from qualtran import (
22-
bloq_example,
23-
BloqDocSpec,
24-
Controlled,
25-
CtrlSpec,
26-
GateWithRegisters,
27-
Signature,
28-
Soquet,
29-
)
30-
from qualtran.bloqs.basic_gates import SU2RotationGate
21+
from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, Signature, Soquet
3122
from qualtran.bloqs.qsp.generalized_qsp import GeneralizedQSP, scale_down_to_qsp_polynomial
3223
from qualtran.bloqs.qubitization_walk_operator import QubitizationWalkOperator
3324
from qualtran.linalg.jacobi_anger_approximations import (
@@ -36,13 +27,13 @@
3627
)
3728
from qualtran.resource_counting.symbolic_counting_utils import (
3829
is_symbolic,
30+
Shaped,
3931
SymbolicFloat,
4032
SymbolicInt,
4133
)
4234

4335
if TYPE_CHECKING:
44-
from qualtran import BloqBuilder, Soquet, SoquetT
45-
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
36+
from qualtran import BloqBuilder, SoquetT
4637

4738

4839
@frozen
@@ -92,7 +83,7 @@ class HamiltonianSimulationByGQSP(GateWithRegisters):
9283
9384
References:
9485
[Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501)
95-
Motlagh and Wiebe. (2023). Theorem 7, Corollary 8.
86+
Motlagh and Wiebe. (2023). Theorem 7, Corollary 8.
9687
9788
Args:
9889
walk_operator: qubitization walk operator of $H$ constructed from SELECT and PREPARE oracles.
@@ -106,7 +97,7 @@ class HamiltonianSimulationByGQSP(GateWithRegisters):
10697
alpha: SymbolicFloat = field(kw_only=True)
10798
precision: SymbolicFloat = field(kw_only=True)
10899

109-
def _parameterized_(self):
100+
def is_symbolic(self):
110101
return is_symbolic(self.t, self.alpha, self.precision)
111102

112103
@cached_property
@@ -115,10 +106,11 @@ def degree(self) -> SymbolicInt:
115106
return degree_jacobi_anger_approximation(self.t * self.alpha, precision=self.precision)
116107

117108
@cached_property
118-
def approx_cos(self) -> NDArray[np.complex_]:
109+
def approx_cos(self) -> Union[NDArray[np.complex_], Shaped]:
119110
r"""polynomial approximation for $$e^{i\theta} \mapsto e^{it\cos(\theta)}$$"""
120-
if self._parameterized_():
121-
raise ValueError(f"cannot compute `cos` approximation for parameterized Bloq {self}")
111+
if self.is_symbolic():
112+
return Shaped((2 * self.degree + 1,))
113+
122114
poly = approx_exp_cos_by_jacobi_anger(-self.t * self.alpha, degree=self.degree)
123115
# TODO(#860) current scaling method does not compute true maximum, so we scale down a bit more by (1 - 2\eps)
124116
poly = scale_down_to_qsp_polynomial(poly) * (1 - 2 * self.precision)
@@ -129,7 +121,7 @@ def gqsp(self) -> GeneralizedQSP:
129121
return GeneralizedQSP.from_qsp_polynomial(
130122
self.walk_operator,
131123
self.approx_cos,
132-
negative_power=int(self.degree),
124+
negative_power=self.degree,
133125
verify=True,
134126
verify_precision=1e-4,
135127
)
@@ -180,18 +172,6 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str
180172

181173
return soqs
182174

183-
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']:
184-
if self._parameterized_():
185-
d = self.degree
186-
return {
187-
(Controlled(self.walk_operator.adjoint(), CtrlSpec()), d),
188-
(Controlled(self.walk_operator, CtrlSpec(cvs=0)), d),
189-
(self.walk_operator.prepare, 1),
190-
(self.walk_operator.prepare.adjoint(), 1),
191-
(SU2RotationGate.arbitrary(ssa), 2 * d + 1),
192-
}
193-
return self.decompose_bloq().build_call_graph(ssa)
194-
195175

196176
@bloq_example
197177
def _hubbard_time_evolution_by_gqsp() -> HamiltonianSimulationByGQSP:
@@ -204,8 +184,22 @@ def _hubbard_time_evolution_by_gqsp() -> HamiltonianSimulationByGQSP:
204184
return hubbard_time_evolution_by_gqsp
205185

206186

187+
@bloq_example
188+
def _symbolic_hamsim_by_gqsp() -> HamiltonianSimulationByGQSP:
189+
import sympy
190+
191+
from qualtran.bloqs.hubbard_model import get_walk_operator_for_hubbard_model
192+
193+
walk_op = get_walk_operator_for_hubbard_model(2, 2, 1, 1)
194+
t, alpha, inv_eps = sympy.symbols("t alpha N")
195+
symbolic_hamsim_by_gqsp = HamiltonianSimulationByGQSP(
196+
walk_op, t=t, alpha=alpha, precision=1 / inv_eps
197+
)
198+
return symbolic_hamsim_by_gqsp
199+
200+
207201
_Hamiltonian_Simulation_by_GQSP_DOC = BloqDocSpec(
208202
bloq_cls=HamiltonianSimulationByGQSP,
209203
import_line='from qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp import HamiltonianSimulationByGQSP',
210-
examples=[_hubbard_time_evolution_by_gqsp],
204+
examples=[_hubbard_time_evolution_by_gqsp, _symbolic_hamsim_by_gqsp],
211205
)

qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
from .hamiltonian_simulation_by_gqsp import (
3131
_hubbard_time_evolution_by_gqsp,
32+
_symbolic_hamsim_by_gqsp,
3233
HamiltonianSimulationByGQSP,
3334
)
3435

@@ -37,6 +38,12 @@ def test_examples(bloq_autotester):
3738
bloq_autotester(_hubbard_time_evolution_by_gqsp)
3839

3940

41+
def test_symbolic_examples(bloq_autotester):
42+
if bloq_autotester.check_name == 'serialization':
43+
pytest.xfail(f"serialization for {_symbolic_hamsim_by_gqsp.name} not yet supported")
44+
bloq_autotester(_symbolic_hamsim_by_gqsp)
45+
46+
4047
@pytest.mark.slow
4148
@pytest.mark.parametrize("bitsize", [1, 2])
4249
@pytest.mark.parametrize("t", [2, 3, 5, 10])

qualtran/bloqs/qsp/generalized_qsp.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
" - `negative_power`: value of $k$, which effectively applies $z^{-k} P(z)$. defaults to 0. \n",
8888
"\n",
8989
"#### References\n",
90-
" - [Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501). Motlagh and Wiebe. (2023). Theorem 3; Figure 2; Theorem 6.\n"
90+
" - [Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501). Motlagh and Wiebe. (2023). Theorem 3; Figure 2; Theorem 6.\n"
9191
]
9292
},
9393
{

qualtran/bloqs/qsp/generalized_qsp.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
from collections import Counter
1515
from functools import cached_property
16-
from typing import Iterable, Sequence, Set, Tuple, TYPE_CHECKING
16+
from typing import Iterable, Sequence, Set, Tuple, TYPE_CHECKING, Union
1717

1818
import numpy as np
1919
from attrs import field, frozen
@@ -22,6 +22,14 @@
2222

2323
from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, QBit, Register, Signature
2424
from qualtran.bloqs.basic_gates.su2_rotation import SU2RotationGate
25+
from qualtran.resource_counting.symbolic_counting_utils import (
26+
is_symbolic,
27+
Shaped,
28+
slen,
29+
smax,
30+
smin,
31+
SymbolicInt,
32+
)
2533

2634
if TYPE_CHECKING:
2735
import cirq
@@ -253,8 +261,10 @@ def assert_is_qsp_polynomial(P: Sequence[complex], *, n_points: int = 2**17):
253261
), f"Not a QSP polynomial! maximum absolute value {max_value} is greater than 1."
254262

255263

256-
def _to_tuple(x: Iterable[complex]) -> Sequence[complex]:
264+
def _to_tuple(x: Iterable[complex]) -> Union[Tuple[complex, ...], Shaped]:
257265
"""mypy-compatible attrs converter for GeneralizedQSP.P and Q"""
266+
if isinstance(x, Shaped):
267+
return x
258268
return tuple(x)
259269

260270

@@ -312,18 +322,19 @@ class GeneralizedQSP(GateWithRegisters):
312322
313323
References:
314324
[Generalized Quantum Signal Processing](https://arxiv.org/abs/2308.01501)
315-
Motlagh and Wiebe. (2023). Theorem 3; Figure 2; Theorem 6.
325+
Motlagh and Wiebe. (2023). Theorem 3; Figure 2; Theorem 6.
316326
"""
317327

318328
U: GateWithRegisters
319-
P: Tuple[complex, ...] = field(converter=_to_tuple)
320-
Q: Tuple[complex, ...] = field(converter=_to_tuple)
321-
negative_power: int = field(default=0, kw_only=True)
329+
P: Union[Tuple[complex, ...], Shaped] = field(converter=_to_tuple)
330+
Q: Union[Tuple[complex, ...], Shaped] = field(converter=_to_tuple)
331+
negative_power: SymbolicInt = field(default=0, kw_only=True)
322332

323333
@P.validator
324334
@Q.validator
325335
def _check_polynomial(self, attribute, value):
326-
if len(value) <= 1:
336+
degree = slen(value) - 1
337+
if not is_symbolic(degree) and degree <= 0:
327338
raise ValueError("GQSP Polynomial must have degree at least 1")
328339

329340
@cached_property
@@ -334,12 +345,15 @@ def signature(self) -> Signature:
334345
def from_qsp_polynomial(
335346
cls,
336347
U: GateWithRegisters,
337-
P: Sequence[complex],
348+
P: Union[Sequence[complex], Shaped],
338349
*,
339350
negative_power: int = 0,
340351
verify: bool = False,
341352
verify_precision=1e-7,
342353
) -> 'GeneralizedQSP':
354+
if is_symbolic(P):
355+
return GeneralizedQSP(U, P, P, negative_power=negative_power)
356+
343357
if verify:
344358
assert_is_qsp_polynomial(P)
345359
Q = qsp_complementary_polynomial(P, verify=verify, verify_precision=verify_precision)
@@ -391,7 +405,20 @@ def decompose_from_registers(
391405
for _ in range(num_inverse_applications):
392406
yield self.U.adjoint().on_registers(**quregs)
393407

408+
def is_symbolic(self) -> bool:
409+
return is_symbolic(self.P, self.Q, self.negative_power)
410+
394411
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']:
412+
if self.is_symbolic():
413+
degree = slen(self.P) - 1
414+
415+
return {
416+
(self.U.controlled(control_values=[0]), smax(0, degree - self.negative_power)),
417+
(self.U.adjoint(), smax(0, self.negative_power - degree)),
418+
(self.U.adjoint().controlled(), smin(degree, self.negative_power)),
419+
(SU2RotationGate.arbitrary(ssa), degree + 1),
420+
}
421+
395422
degree = len(self.P) - 1
396423

397424
counts = set(Counter(self.signal_rotations).items())

0 commit comments

Comments
 (0)