-
Notifications
You must be signed in to change notification settings - Fork 36
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
PauliZGate #254
Merged
Merged
PauliZGate #254
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
2a5ab38
Pauli Z Matrices for generating diagonal unitaries
mtweiden 886f4f6
Operations for PauliZ
mtweiden fa816e5
PauliZGate for diagonal unitaries
mtweiden 9c98027
Added PauliZ to __init__
mtweiden f155efc
Added diagonal distance
mtweiden 00843e3
Tests for diagonal distance
mtweiden ff22669
Diagonal predicate
mtweiden ff23b38
pre-commit fixes
mtweiden 873868c
Docstring fix
mtweiden 88a84d4
DiagonalSynthesisPass
mtweiden 5b8392c
Documentation for DiagonalSynthesisPass
mtweiden 6d71816
Pre-commit
mtweiden d9fe820
Updated documentation
mtweiden 0f7a872
Updated documentation
mtweiden 9bcda45
Faster get_unitary
mtweiden 0bc4a4e
Updated documentation
mtweiden 01d5bcd
Added reference, now a BasePass
mtweiden 40b190b
Added TODO note for qudit support
mtweiden 7363ef7
Changed to SynthesisPass
mtweiden db69e48
pre-commit
mtweiden 0c2d0bd
Fixed SynthesisPass import
mtweiden 09c02c8
Renamed DiagonalSynthesisPass to WalshDiagonalSynthesisPass
mtweiden File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,6 +73,7 @@ | |
CUGate | ||
FSIMGate | ||
PauliGate | ||
PauliZGate | ||
PhasedXZGate | ||
RSU3Gate | ||
RXGate | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
"""This module implements the PauliZGate.""" | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
import numpy as np | ||
import numpy.typing as npt | ||
|
||
from bqskit.ir.gates.generalgate import GeneralGate | ||
from bqskit.ir.gates.qubitgate import QubitGate | ||
from bqskit.qis.pauliz import PauliZMatrices | ||
from bqskit.qis.unitary.differentiable import DifferentiableUnitary | ||
from bqskit.qis.unitary.unitary import RealVector | ||
from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix | ||
from bqskit.utils.docs import building_docs | ||
from bqskit.utils.math import dexpmv | ||
from bqskit.utils.math import dot_product | ||
from bqskit.utils.math import pauliz_expansion | ||
from bqskit.utils.math import unitary_log_no_i | ||
|
||
|
||
class PauliZGate(QubitGate, DifferentiableUnitary, GeneralGate): | ||
""" | ||
A gate representing an arbitrary diagonal rotation. | ||
|
||
This gate is given by: | ||
|
||
.. math:: | ||
|
||
\\exp({i(\\vec{\\alpha} \\cdot \\vec{\\sigma_Z^{\\otimes n}})}) | ||
|
||
Where :math:`\\vec{\\alpha}` are the gate's parameters, | ||
:math:`\\vec{\\sigma}` are the PauliZ Z matrices, | ||
and :math:`n` is the number of qubits this gate acts on. | ||
""" | ||
|
||
def __init__(self, num_qudits: int) -> None: | ||
""" | ||
Create a PauliZGate acting on `num_qudits` qubits. | ||
|
||
Args: | ||
num_qudits (int): The number of qudits this gate will act on. | ||
|
||
Raises: | ||
ValueError: If `num_qudits` is nonpositive. | ||
""" | ||
|
||
if num_qudits <= 0: | ||
raise ValueError(f'Expected positive integer, got {num_qudits}') | ||
|
||
self._name = f'PauliZGate({num_qudits})' | ||
self._num_qudits = num_qudits | ||
paulizs = PauliZMatrices(self.num_qudits) | ||
self._num_params = len(paulizs) | ||
if building_docs(): | ||
self.sigmav: npt.NDArray[Any] = np.array([]) | ||
else: | ||
self.sigmav = (-1j / 2) * paulizs.numpy | ||
|
||
def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: | ||
"""Return the unitary for this gate, see :class:`Unitary` for more.""" | ||
self.check_parameters(params) | ||
H = dot_product(params, self.sigmav) | ||
eiH = np.diag(np.exp(np.diag(H))) | ||
return UnitaryMatrix(eiH, check_arguments=False) | ||
|
||
def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: | ||
""" | ||
Return the gradient for this gate. | ||
|
||
See :class:`DifferentiableUnitary` for more info. | ||
|
||
TODO: Accelerated gradient computation for diagonal matrices. | ||
""" | ||
self.check_parameters(params) | ||
H = dot_product(params, self.sigmav) | ||
_, dU = dexpmv(H, self.sigmav) | ||
return dU | ||
|
||
def get_unitary_and_grad( | ||
self, | ||
params: RealVector = [], | ||
) -> tuple[UnitaryMatrix, npt.NDArray[np.complex128]]: | ||
""" | ||
Return the unitary and gradient for this gate. | ||
|
||
See :class:`DifferentiableUnitary` for more info. | ||
""" | ||
self.check_parameters(params) | ||
|
||
H = dot_product(params, self.sigmav) | ||
U, dU = dexpmv(H, self.sigmav) | ||
return UnitaryMatrix(U, check_arguments=False), dU | ||
|
||
def calc_params(self, utry: UnitaryMatrix) -> list[float]: | ||
"""Return the parameters for this gate to implement `utry`""" | ||
return list(-2 * pauliz_expansion(unitary_log_no_i(utry.numpy))) | ||
|
||
def __eq__(self, o: object) -> bool: | ||
return isinstance(o, PauliZGate) and self.num_qudits == o.num_qudits | ||
|
||
def __hash__(self) -> int: | ||
return hash((self.__class__.__name__, self.num_qudits)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
"""This module implements the DiagonalPredicate class.""" | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from bqskit.passes.control.predicate import PassPredicate | ||
from bqskit.utils.math import diagonal_distance | ||
|
||
if TYPE_CHECKING: | ||
from bqskit.compiler.passdata import PassData | ||
from bqskit.ir.circuit import Circuit | ||
|
||
|
||
class DiagonalPredicate(PassPredicate): | ||
""" | ||
The DiagonalPredicate class. | ||
|
||
The DiagonalPredicate class returns True if the circuit's unitary can be | ||
approximately inverted by a diagonal unitary. A unitary is approximately | ||
inverted when the Hilbert-Schmidt distance to the identity is less than some | ||
threshold. | ||
""" | ||
|
||
def __init__(self, threshold: float) -> None: | ||
""" | ||
Construct a DiagonalPredicate. | ||
|
||
Args: | ||
threshold (float): If a circuit can be approximately inverted | ||
by a diagonal unitary (meaning the Hilbert-Schmidt distance | ||
to the identity is less than or equal to this number after | ||
multiplying by the diagonal unitary), True is returned. | ||
""" | ||
self.threshold = threshold | ||
|
||
def get_truth_value(self, circuit: Circuit, data: PassData) -> bool: | ||
"""Call this predicate, see :class:`PassPredicate` for more info.""" | ||
dist = diagonal_distance(circuit.get_unitary().numpy) | ||
return dist <= self.threshold |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
"""This module implements the WalshDiagonalSynthesisPass.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
|
||
from numpy import where | ||
|
||
from bqskit.compiler.passdata import PassData | ||
from bqskit.ir.circuit import Circuit | ||
from bqskit.ir.gates import CNOTGate | ||
from bqskit.ir.gates import RZGate | ||
from bqskit.passes.synthesis.synthesis import SynthesisPass | ||
from bqskit.qis.state.state import StateVector | ||
from bqskit.qis.state.system import StateSystem | ||
from bqskit.qis.unitary import UnitaryMatrix | ||
from bqskit.utils.math import pauliz_expansion | ||
from bqskit.utils.math import unitary_log_no_i | ||
|
||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class WalshDiagonalSynthesisPass(SynthesisPass): | ||
""" | ||
A pass that synthesizes diagonal unitaries into Walsh functions. | ||
|
||
Based on: https://arxiv.org/abs/1306.3991 | ||
""" | ||
|
||
def __init__( | ||
self, | ||
parameter_precision: float = 1e-8, | ||
) -> None: | ||
""" | ||
Constructor for WalshDiagonalSynthesisPass. | ||
|
||
Args: | ||
parameter_precision (float): Pauli strings with parameter values | ||
less than this are rounded to zero. (Default: 1e-8) | ||
|
||
TODO: | ||
- Cancel adjacent CNOTs | ||
- See how QFAST can be used to generalize to qudits | ||
""" | ||
self.parameter_precision = parameter_precision | ||
|
||
def gray_code(self, number: int) -> int: | ||
"""Convert a number to its Gray code representation.""" | ||
gray = number ^ (number >> 1) | ||
return gray | ||
|
||
def pauli_to_subcircuit( | ||
self, | ||
string_id: int, | ||
angle: float, | ||
num_qubits: int, | ||
) -> Circuit: | ||
string = bin(string_id)[2:].zfill(num_qubits) | ||
circuit = Circuit(num_qubits) | ||
locations = [i for i in range(num_qubits) if string[i] == '1'] | ||
if len(locations) == 1: | ||
circuit.append_gate(RZGate(), locations[0], [angle]) | ||
elif len(locations) > 1: | ||
pairs = [ | ||
(locations[i], locations[i + 1]) | ||
for i in range(len(locations) - 1) | ||
] | ||
for pair in pairs: | ||
circuit.append_gate(CNOTGate(), pair) | ||
circuit.append_gate(RZGate(), locations[-1], [angle]) | ||
for pair in reversed(pairs): | ||
circuit.append_gate(CNOTGate(), pair) | ||
return circuit | ||
|
||
async def synthesize( | ||
self, | ||
utry: UnitaryMatrix | StateVector | StateSystem, | ||
data: PassData, | ||
) -> Circuit: | ||
"""Synthesize `utry`, see :class:`SynthesisPass` for more.""" | ||
if not isinstance(utry, UnitaryMatrix): | ||
m = 'WalshDiagonalSynthesisPass can only synthesize diagonal, ' | ||
m += f'`UnitaryMatrix`s, got {type(utry)}.' | ||
raise TypeError(m) | ||
|
||
if not utry.is_qubit_only(): | ||
m = 'WalshDiagonalSynthesisPass can only synthesize diagonal ' | ||
m += '`UnitaryMatrix`s with qubits, got higher radix than 2.' | ||
raise ValueError(m) | ||
|
||
num_qubits = utry.num_qudits | ||
circuit = Circuit(num_qubits) | ||
|
||
# Find parameters of each I/Z Pauli string | ||
H_matrix = unitary_log_no_i(utry.numpy) | ||
params = pauliz_expansion(H_matrix) * 2 | ||
# Remove low weight terms - these are likely numerical errors | ||
params = where(abs(params) < self.parameter_precision, 0, params) | ||
|
||
# Order the Pauli strings by their Gray code representation | ||
pauli_params = sorted( | ||
[(i, -p) for i, p in enumerate(params)], | ||
key=lambda x: self.gray_code(x[0]), | ||
) | ||
subcircuits = [ | ||
self.pauli_to_subcircuit(i, p, num_qubits) for i, p in pauli_params | ||
] | ||
|
||
for subcircuit in subcircuits: | ||
circuit.append_circuit(subcircuit, [_ for _ in range(num_qubits)]) | ||
|
||
return circuit |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to put a "See Also" section in the documentation.