Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ScaledChebyshevPolynomial bloq #1231

Merged
merged 7 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dev_tools/autogenerate-bloqs-notebooks-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,10 @@
NotebookSpecV2(
title='Chebyshev Polynomial',
module=qualtran.bloqs.block_encoding.chebyshev_polynomial,
bloq_specs=[qualtran.bloqs.block_encoding.chebyshev_polynomial._CHEBYSHEV_BLOQ_DOC],
bloq_specs=[
qualtran.bloqs.block_encoding.chebyshev_polynomial._CHEBYSHEV_BLOQ_DOC,
qualtran.bloqs.block_encoding.chebyshev_polynomial._SCALED_CHEBYSHEV_BLOQ_DOC,
],
),
NotebookSpecV2(
title='LCU Select/Prepare Oracles',
Expand Down
5 changes: 4 additions & 1 deletion qualtran/bloqs/block_encoding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
r"""High level bloqs for defining bloq encodings and operations on block encodings."""

from qualtran.bloqs.block_encoding.block_encoding_base import BlockEncoding
from qualtran.bloqs.block_encoding.chebyshev_polynomial import ChebyshevPolynomial
from qualtran.bloqs.block_encoding.chebyshev_polynomial import (
ChebyshevPolynomial,
ScaledChebyshevPolynomial,
)
from qualtran.bloqs.block_encoding.lcu_block_encoding import (
BlackBoxPrepare,
BlackBoxSelect,
Expand Down
140 changes: 132 additions & 8 deletions qualtran/bloqs/block_encoding/chebyshev_polynomial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,9 @@
},
"source": [
"## `ChebyshevPolynomial`\n",
"Block encoding of $T_j[A]$ where $T_j$ is the $j$-th Chebyshev polynomial.\n",
"Block encoding of $T_j(A / \\alpha)$ where $T_j$ is the $j$-th Chebyshev polynomial.\n",
"\n",
"Here $A$ is a Hermitian matrix with spectral norm $|A| \\le 1$, we assume we have\n",
"an $n_L$ qubit ancilla register, and assume that $j > 0$ to avoid block\n",
"encoding the identity operator.\n",
"\n",
"Recall:\n",
"Given a Hermitian matrix $A$ with spectral norm $|A| \\le 1$, recall:\n",
"\n",
"\\begin{align*}\n",
" T_0[A] &= I \\\\\n",
Expand All @@ -55,7 +51,7 @@
"If `block_encoding` block encodes $A$ with normalization factor $\\alpha$, i.e. it constructs\n",
"$\\mathcal{B}[A/\\alpha]$, then this bloq constructs $\\mathcal{B}[T_j(A/\\alpha)]$ with\n",
"normalization factor 1. Note that $\\mathcal{B}[T_j(A/\\alpha)]$ is not a multiple of\n",
"$\\mathcal{B}[T_j(A)]$ in general.\n",
"$\\mathcal{B}[T_j(A)]$ in general; use `ScaledChebyshevPolynomial` if $\\alpha \\neq 1$.\n",
"\n",
"See https://github.com/quantumlib/Qualtran/issues/984 for an alternative.\n",
"\n",
Expand Down Expand Up @@ -101,7 +97,7 @@
"from qualtran.bloqs.basic_gates import Hadamard, XGate\n",
"from qualtran.bloqs.block_encoding import LinearCombination, Unitary\n",
"\n",
"bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (1.0, 1.0), lambd_bits=1)\n",
"bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (0.5, 0.5), lambd_bits=1)\n",
"chebyshev_poly_even = ChebyshevPolynomial(bloq, order=4)"
]
},
Expand Down Expand Up @@ -169,6 +165,134 @@
"show_call_graph(chebyshev_poly_even_g)\n",
"show_counts_sigma(chebyshev_poly_even_sigma)"
]
},
{
"cell_type": "markdown",
"id": "f30b1a5d",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.bloq_doc.md"
},
"source": [
"## `ScaledChebyshevPolynomial`\n",
"Block encoding of $T_j(A)$ where $T_j$ is the $j$-th Chebyshev polynomial.\n",
"\n",
"Unlike `ChebyshevPolynomial`, this bloq accepts $\\mathcal{B}[A/\\alpha]$ with $\\alpha \\neq 1$\n",
"and constructs $\\mathcal{B}[T_j(A)]$ which is not a multiple of $\\mathcal{B}[T_j(A/\\alpha)]$\n",
"in general. It does so by constructing $T_k(t)$ in terms of $T_j(t/\\alpha)$ for $j \\in [0, k]$.\n",
"\n",
"#### Parameters\n",
" - `block_encoding`: Block encoding of a Hermitian matrix $A$, $\\mathcal{B}[A/\\alpha]$. Assumes the $|G\\rangle$ state of the block encoding is the identity operator.\n",
" - `order`: order of Chebychev polynomial.\n",
" - `lambd_bits`: number of bits to represent coefficients of linear combination precisely. \n",
"\n",
"#### References\n",
" - [Explicit Quantum Circuits for Block Encodings of Certain Sparse Matrices](https://arxiv.org/abs/2203.10236). Camps et al. (2023). Section 5.1.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508faa91",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.block_encoding import ScaledChebyshevPolynomial"
]
},
{
"cell_type": "markdown",
"id": "338fcd02",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3cae55ac",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.scaled_chebyshev_poly_even"
},
"outputs": [],
"source": [
"from qualtran.bloqs.basic_gates import Hadamard, XGate\n",
"from qualtran.bloqs.block_encoding import LinearCombination, Unitary\n",
"\n",
"bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (1.0, 1.0), lambd_bits=1)\n",
"scaled_chebyshev_poly_even = ScaledChebyshevPolynomial(bloq, order=4)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4e2aa89f",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.scaled_chebyshev_poly_odd"
},
"outputs": [],
"source": [
"from attrs import evolve\n",
"\n",
"from qualtran.bloqs.basic_gates import Hadamard\n",
"from qualtran.bloqs.block_encoding import Unitary\n",
"\n",
"bloq = evolve(Unitary(Hadamard()), alpha=3.14)\n",
"scaled_chebyshev_poly_odd = ScaledChebyshevPolynomial(bloq, order=5)"
]
},
{
"cell_type": "markdown",
"id": "76887fe8",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d83706cf",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([scaled_chebyshev_poly_even, scaled_chebyshev_poly_odd],\n",
" ['`scaled_chebyshev_poly_even`', '`scaled_chebyshev_poly_odd`'])"
]
},
{
"cell_type": "markdown",
"id": "901aebb5",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.call_graph.md"
},
"source": [
"### Call Graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "179cf0a7",
"metadata": {
"cq.autogen": "ScaledChebyshevPolynomial.call_graph.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"scaled_chebyshev_poly_even_g, scaled_chebyshev_poly_even_sigma = scaled_chebyshev_poly_even.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
"show_call_graph(scaled_chebyshev_poly_even_g)\n",
"show_counts_sigma(scaled_chebyshev_poly_even_sigma)"
]
}
],
"metadata": {
Expand Down
144 changes: 133 additions & 11 deletions qualtran/bloqs/block_encoding/chebyshev_polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
# limitations under the License.

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

import attrs
import numpy as np

from qualtran import (
bloq_example,
Expand All @@ -30,6 +31,7 @@
)
from qualtran.bloqs.basic_gates.global_phase import GlobalPhase
from qualtran.bloqs.block_encoding import BlockEncoding
from qualtran.bloqs.block_encoding.linear_combination import LinearCombination
from qualtran.bloqs.state_preparation.prepare_base import PrepareOracle
from qualtran.symbolics import is_symbolic, SymbolicFloat, SymbolicInt

Expand All @@ -39,13 +41,9 @@

@attrs.frozen
class ChebyshevPolynomial(BlockEncoding):
r"""Block encoding of $T_j[A]$ where $T_j$ is the $j$-th Chebyshev polynomial.
r"""Block encoding of $T_j(A / \alpha)$ where $T_j$ is the $j$-th Chebyshev polynomial.
Here $A$ is a Hermitian matrix with spectral norm $|A| \le 1$, we assume we have
an $n_L$ qubit ancilla register, and assume that $j > 0$ to avoid block
encoding the identity operator.
Recall:
Given a Hermitian matrix $A$ with spectral norm $|A| \le 1$, recall:
\begin{align*}
T_0[A] &= I \\
Expand All @@ -58,7 +56,7 @@ class ChebyshevPolynomial(BlockEncoding):
If `block_encoding` block encodes $A$ with normalization factor $\alpha$, i.e. it constructs
$\mathcal{B}[A/\alpha]$, then this bloq constructs $\mathcal{B}[T_j(A/\alpha)]$ with
normalization factor 1. Note that $\mathcal{B}[T_j(A/\alpha)]$ is not a multiple of
$\mathcal{B}[T_j(A)]$ in general.
$\mathcal{B}[T_j(A)]$ in general; use `ScaledChebyshevPolynomial` if $\alpha \neq 1$.
See https://github.com/quantumlib/Qualtran/issues/984 for an alternative.
Expand All @@ -76,8 +74,8 @@ class ChebyshevPolynomial(BlockEncoding):
order: int

def __attrs_post_init__(self):
if self.order < 1:
raise ValueError(f"order must be greater >= 1. Found {self.order}.")
if self.order < 0:
raise ValueError(f"order must be greater >= 0. Found {self.order}.")

@cached_property
def signature(self) -> Signature:
Expand Down Expand Up @@ -167,7 +165,7 @@ def _chebyshev_poly_even() -> ChebyshevPolynomial:
from qualtran.bloqs.basic_gates import Hadamard, XGate
from qualtran.bloqs.block_encoding import LinearCombination, Unitary

bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (1.0, 1.0), lambd_bits=1)
bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (0.5, 0.5), lambd_bits=1)
chebyshev_poly_even = ChebyshevPolynomial(bloq, order=4)
return chebyshev_poly_even

Expand All @@ -185,3 +183,127 @@ def _chebyshev_poly_odd() -> ChebyshevPolynomial:
_CHEBYSHEV_BLOQ_DOC = BloqDocSpec(
bloq_cls=ChebyshevPolynomial, examples=(_chebyshev_poly_even, _chebyshev_poly_odd)
)


@attrs.frozen
class ScaledChebyshevPolynomial(BlockEncoding):
r"""Block encoding of $T_j(A)$ where $T_j$ is the $j$-th Chebyshev polynomial.
Unlike `ChebyshevPolynomial`, this bloq accepts $\mathcal{B}[A/\alpha]$ with $\alpha \neq 1$
and constructs $\mathcal{B}[T_j(A)]$ which is not a multiple of $\mathcal{B}[T_j(A/\alpha)]$
in general. It does so by constructing $T_k(t)$ in terms of $T_j(t/\alpha)$ for $j \in [0, k]$.
Args:
block_encoding: Block encoding of a Hermitian matrix $A$, $\mathcal{B}[A/\alpha]$.
Assumes the $|G\rangle$ state of the block encoding is the identity operator.
order: order of Chebychev polynomial.
lambd_bits: number of bits to represent coefficients of linear combination precisely.
References:
[Explicit Quantum Circuits for Block Encodings of Certain Sparse Matrices](https://arxiv.org/abs/2203.10236). Camps et al. (2023). Section 5.1.
"""

block_encoding: BlockEncoding
order: int
lambd_bits: int = 5

def __attrs_post_init__(self):
if self.order < 0:
raise ValueError(f"order must be greater >= 0. Found {self.order}.")

@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 resource_bitsize is 0, not present
)

def pretty_name(self) -> str:
return f"B[T_{self.order}({self.block_encoding.pretty_name()})]"

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

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

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

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

@property
def epsilon(self) -> SymbolicFloat:
return self.linear_combination.epsilon

@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

@cached_property
def linear_combination(self) -> Union[LinearCombination, ChebyshevPolynomial]:
if self.order <= 1:
return ChebyshevPolynomial(self.block_encoding, self.order)

coeffs = np.polynomial.chebyshev.cheb2poly([0] * self.order + [1])
for i in range(len(coeffs)):
coeffs[i] *= self.block_encoding.alpha**i
cheb_coeffs = np.polynomial.chebyshev.poly2cheb(coeffs)

return LinearCombination(
tuple(ChebyshevPolynomial(self.block_encoding, i) for i in range(self.order + 1)),
cheb_coeffs,
self.lambd_bits,
)

def build_composite_bloq(self, bb: BloqBuilder, **soqs: SoquetT) -> Dict[str, SoquetT]:
return bb.add_d(self.linear_combination, **soqs)


@bloq_example
def _scaled_chebyshev_poly_even() -> ScaledChebyshevPolynomial:
from qualtran.bloqs.basic_gates import Hadamard, XGate
from qualtran.bloqs.block_encoding import LinearCombination, Unitary

bloq = LinearCombination((Unitary(XGate()), Unitary(Hadamard())), (1.0, 1.0), lambd_bits=1)
scaled_chebyshev_poly_even = ScaledChebyshevPolynomial(bloq, order=4)
return scaled_chebyshev_poly_even


@bloq_example
def _scaled_chebyshev_poly_odd() -> ScaledChebyshevPolynomial:
from attrs import evolve

from qualtran.bloqs.basic_gates import Hadamard
from qualtran.bloqs.block_encoding import Unitary

bloq = evolve(Unitary(Hadamard()), alpha=3.14)
scaled_chebyshev_poly_odd = ScaledChebyshevPolynomial(bloq, order=5)
return scaled_chebyshev_poly_odd


_SCALED_CHEBYSHEV_BLOQ_DOC = BloqDocSpec(
bloq_cls=ScaledChebyshevPolynomial,
examples=(_scaled_chebyshev_poly_even, _scaled_chebyshev_poly_odd),
)
Loading
Loading