Skip to content

Commit

Permalink
Add Phase block encoding (#1129)
Browse files Browse the repository at this point in the history
* Add `Negate` block encoding

* Change to `GlobalPhase`

* Simplify docstring
  • Loading branch information
charlesyuan314 authored Jul 12, 2024
1 parent 2bd4f05 commit f8cebdb
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 0 deletions.
2 changes: 2 additions & 0 deletions dev_tools/autogenerate-bloqs-notebooks-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import qualtran.bloqs.block_encoding.chebyshev_polynomial
import qualtran.bloqs.block_encoding.lcu_block_encoding
import qualtran.bloqs.block_encoding.lcu_select_and_prepare
import qualtran.bloqs.block_encoding.phase
import qualtran.bloqs.bookkeeping
import qualtran.bloqs.chemistry.df.double_factorization
import qualtran.bloqs.chemistry.hubbard_model.qubitization
Expand Down Expand Up @@ -591,6 +592,7 @@
qualtran.bloqs.block_encoding.unitary._UNITARY_DOC,
qualtran.bloqs.block_encoding.tensor_product._TENSOR_PRODUCT_DOC,
qualtran.bloqs.block_encoding.product._PRODUCT_DOC,
qualtran.bloqs.block_encoding.phase._PHASE_DOC,
],
directory=f'{SOURCE_DIR}/bloqs/block_encoding/',
),
Expand Down
1 change: 1 addition & 0 deletions qualtran/bloqs/block_encoding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
LCUBlockEncoding,
LCUBlockEncodingZeroState,
)
from qualtran.bloqs.block_encoding.phase import Phase
from qualtran.bloqs.block_encoding.product import Product
from qualtran.bloqs.block_encoding.tensor_product import TensorProduct
from qualtran.bloqs.block_encoding.unitary import Unitary
110 changes: 110 additions & 0 deletions qualtran/bloqs/block_encoding/block_encoding.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,116 @@
"show_call_graph(product_block_encoding_g)\n",
"show_counts_sigma(product_block_encoding_sigma)"
]
},
{
"cell_type": "markdown",
"id": "e9f1d56b",
"metadata": {
"cq.autogen": "Phase.bloq_doc.md"
},
"source": [
"## `Phase`\n",
"Apply a phase to a block encoding.\n",
"\n",
"Given $B[A]$ as a $(\\alpha, a, \\epsilon)$-block encoding of $A$, produces a\n",
"$(\\alpha, a, \\epsilon)$-block encoding $B[\\exp(i\\pi\\phi)A]$ of $\\exp(i\\pi\\phi)A$.\n",
"\n",
"#### Parameters\n",
" - `block_encoding`: The block encoding to apply a phase to.\n",
" - `phi`: The phase angle.\n",
" - `eps`: The precision of the phase angle. \n",
"\n",
"#### Registers\n",
" - `system`: The system register.\n",
" - `ancilla`: The ancilla register (present only if bitsize > 0).\n",
" - `resource`: The resource register (present only if bitsize > 0).\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bffb0d88",
"metadata": {
"cq.autogen": "Phase.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.block_encoding import Phase"
]
},
{
"cell_type": "markdown",
"id": "134b2b46",
"metadata": {
"cq.autogen": "Phase.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ad0eeadd",
"metadata": {
"cq.autogen": "Phase.phase_block_encoding"
},
"outputs": [],
"source": [
"from qualtran.bloqs.basic_gates import Hadamard\n",
"from qualtran.bloqs.block_encoding.unitary import Unitary\n",
"\n",
"phase_block_encoding = Phase(Unitary(Hadamard()), phi=0.25, eps=0)"
]
},
{
"cell_type": "markdown",
"id": "5d1484bb",
"metadata": {
"cq.autogen": "Phase.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f93026c3",
"metadata": {
"cq.autogen": "Phase.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([phase_block_encoding],\n",
" ['`phase_block_encoding`'])"
]
},
{
"cell_type": "markdown",
"id": "14f00196",
"metadata": {
"cq.autogen": "Phase.call_graph.md"
},
"source": [
"### Call Graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6c015067",
"metadata": {
"cq.autogen": "Phase.call_graph.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"phase_block_encoding_g, phase_block_encoding_sigma = phase_block_encoding.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
"show_call_graph(phase_block_encoding_g)\n",
"show_counts_sigma(phase_block_encoding_sigma)"
]
}
],
"metadata": {
Expand Down
122 changes: 122 additions & 0 deletions qualtran/bloqs/block_encoding/phase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from functools import cached_property
from typing import Dict, Set, Tuple

from attrs import frozen

from qualtran import bloq_example, BloqBuilder, BloqDocSpec, QAny, Register, Signature, SoquetT
from qualtran.bloqs.basic_gates import GlobalPhase
from qualtran.bloqs.block_encoding import BlockEncoding
from qualtran.bloqs.block_encoding.lcu_select_and_prepare import PrepareOracle
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
from qualtran.symbolics import SymbolicFloat, SymbolicInt


@frozen
class Phase(BlockEncoding):
r"""Apply a phase to a block encoding.
Given $B[A]$ as a $(\alpha, a, \epsilon)$-block encoding of $A$, produces a
$(\alpha, a, \epsilon)$-block encoding of $\exp(i\pi\phi)A$.
Args:
block_encoding: The block encoding to apply a phase to.
phi: The phase angle.
eps: The precision of the phase angle.
Registers:
system: The system register.
ancilla: The ancilla register (present only if bitsize > 0).
resource: The resource register (present only if bitsize > 0).
"""

block_encoding: BlockEncoding
phi: SymbolicFloat
eps: SymbolicFloat

@property
def alpha(self) -> SymbolicFloat:
return self.block_encoding.alpha

@property
def system_bitsize(self) -> SymbolicInt:
return self.block_encoding.system_bitsize

@property
def ancilla_bitsize(self) -> SymbolicInt:
return self.block_encoding.ancilla_bitsize

@property
def resource_bitsize(self) -> SymbolicInt:
return self.block_encoding.resource_bitsize

@property
def epsilon(self) -> SymbolicFloat:
return self.block_encoding.epsilon + self.eps

@cached_property
def signature(self) -> Signature:
return Signature.build_from_dtypes(
system=QAny(self.system_bitsize),
ancilla=QAny(self.ancilla_bitsize), # if ancilla_bitsize is 0, not present
resource=QAny(self.resource_bitsize), # if ancilla_bitsize is 0, not present
)

def pretty_name(self) -> str:
return f"B[exp({self.phi}i){self.block_encoding.pretty_name()[2:-1]}]"

@property
def target_registers(self) -> Tuple[Register, ...]:
return tuple(self.signature.rights())

@property
def junk_registers(self) -> Tuple[Register, ...]:
return (self.signature.get_right("resource"),) if self.resource_bitsize > 0 else ()

@property
def selection_registers(self) -> Tuple[Register, ...]:
return (self.signature.get_right("ancilla"),) if self.ancilla_bitsize > 0 else ()

@property
def signal_state(self) -> PrepareOracle:
# This method will be implemented in the future after PrepareOracle
# is updated for the BlockEncoding interface.
# GitHub issue: https://github.com/quantumlib/Qualtran/issues/1104
raise NotImplementedError

def build_call_graph(self, ssa: SympySymbolAllocator) -> Set[BloqCountT]:
return {(self.block_encoding, 1), (GlobalPhase(exponent=self.phi, eps=self.eps), 1)}

def build_composite_bloq(self, bb: BloqBuilder, **soqs: SoquetT) -> Dict[str, SoquetT]:
bb.add(GlobalPhase(exponent=self.phi, eps=self.eps))

return bb.add_d(self.block_encoding, **soqs)


@bloq_example
def _phase_block_encoding() -> Phase:
from qualtran.bloqs.basic_gates import Hadamard
from qualtran.bloqs.block_encoding.unitary import Unitary

phase_block_encoding = Phase(Unitary(Hadamard()), phi=0.25, eps=0)
return phase_block_encoding


_PHASE_DOC = BloqDocSpec(
bloq_cls=Phase,
import_line="from qualtran.bloqs.block_encoding import Phase",
examples=[_phase_block_encoding],
)
50 changes: 50 additions & 0 deletions qualtran/bloqs/block_encoding/phase_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np

from qualtran import QAny, Register, Signature
from qualtran.bloqs.basic_gates import Hadamard
from qualtran.bloqs.block_encoding.phase import _phase_block_encoding, Phase
from qualtran.bloqs.block_encoding.unitary import Unitary


def test_phase(bloq_autotester):
bloq_autotester(_phase_block_encoding)


def test_phase_signature():
assert _phase_block_encoding().signature == Signature([Register("system", QAny(1))])


def test_phase_params():
bloq = _phase_block_encoding()
assert bloq.alpha == 1
assert bloq.epsilon == 0
assert bloq.ancilla_bitsize == 0
assert bloq.resource_bitsize == 0


def test_phase_tensors():
bloq = _phase_block_encoding()
from_gate = np.exp(0.25j * np.pi) * Hadamard().tensor_contract()
from_tensors = bloq.tensor_contract()
np.testing.assert_allclose(from_gate, from_tensors)


def test_negate():
bloq = Phase(Unitary(Hadamard()), phi=1, eps=0)
from_gate = -Hadamard().tensor_contract()
from_tensors = bloq.tensor_contract()
np.testing.assert_allclose(from_gate, from_tensors)
1 change: 1 addition & 0 deletions qualtran/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def assert_bloq_example_serializes_for_pytest(bloq_ex: BloqExample):
'product_block_encoding_properties',
'product_block_encoding_symb',
'apply_lth_bloq',
'phase_block_encoding',
]:
pytest.xfail("Skipping serialization test for bloq examples that cannot yet be serialized.")

Expand Down
2 changes: 2 additions & 0 deletions qualtran/serialization/resolver_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import qualtran.bloqs.basic_gates.z_basis
import qualtran.bloqs.block_encoding
import qualtran.bloqs.block_encoding.lcu_select_and_prepare
import qualtran.bloqs.block_encoding.phase
import qualtran.bloqs.block_encoding.product
import qualtran.bloqs.block_encoding.tensor_product
import qualtran.bloqs.block_encoding.unitary
Expand Down Expand Up @@ -195,6 +196,7 @@
"qualtran.bloqs.block_encoding.unitary.Unitary": qualtran.bloqs.block_encoding.unitary.Unitary,
"qualtran.bloqs.block_encoding.tensor_product.TensorProduct": qualtran.bloqs.block_encoding.tensor_product.TensorProduct,
"qualtran.bloqs.block_encoding.product.Product": qualtran.bloqs.block_encoding.product.Product,
"qualtran.bloqs.block_encoding.phase.phase": qualtran.bloqs.block_encoding.phase.Phase,
"qualtran.bloqs.bookkeeping.allocate.Allocate": qualtran.bloqs.bookkeeping.allocate.Allocate,
"qualtran.bloqs.bookkeeping.arbitrary_clifford.ArbitraryClifford": qualtran.bloqs.bookkeeping.arbitrary_clifford.ArbitraryClifford,
"qualtran.bloqs.bookkeeping.auto_partition.AutoPartition": qualtran.bloqs.bookkeeping.auto_partition.AutoPartition,
Expand Down

0 comments on commit f8cebdb

Please sign in to comment.