-
Notifications
You must be signed in to change notification settings - Fork 103
Add support for the Matsumoto-Amano normal form. #1801
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
Changes from 4 commits
4285f8f
66f759c
8a45698
d7a0b0d
e6921ad
337a5c4
4e153e4
b285caa
2f57634
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,9 +13,10 @@ | |||||
| # limitations under the License. | ||||||
|
|
||||||
| import math | ||||||
| from typing import cast, Mapping, Optional | ||||||
| from typing import cast, Mapping, Optional, Union | ||||||
|
|
||||||
| import cirq | ||||||
| import numpy as np | ||||||
|
|
||||||
| import qualtran.rotation_synthesis.matrix._generation as ctg | ||||||
| import qualtran.rotation_synthesis.matrix._su2_ct as _su2_ct | ||||||
|
|
@@ -79,16 +80,66 @@ def _xz_sequence( | |||||
| return None | ||||||
|
|
||||||
|
|
||||||
| def _matsumoto_amano_sequence(matrix: _su2_ct.SU2CliffordT) -> tuple[str, ...]: | ||||||
| r"""Represents the Clifford+T operator in the Matsumoto-Amano normal form. | ||||||
|
|
||||||
| Returns: | ||||||
| a list of gates matching the regular expression $(T|\eps)(HT|SHT)^*C$, | ||||||
| where C is a Clifford operator, itself represented as a list of H and S gates. | ||||||
| The gates are returned in the order they need to be applied to generate the | ||||||
| input matrix. | ||||||
|
|
||||||
| Raises: | ||||||
| ValueError if the parity matrix doesn't match any of the forms in | ||||||
| Lemma 4.10, https://arxiv.org/abs/1312.6584, or if during the decomposition | ||||||
| an invalid SU2CliffordT matrix is created. | ||||||
| """ | ||||||
| if matrix.det() == 2: | ||||||
| return clifford(matrix) | ||||||
| parity = matrix.bloch_form_parity() | ||||||
| # Parity matrix must have a 0 column, see Lemma 4.10, https://arxiv.org/abs/1312.6584. | ||||||
| # We move it to be last. | ||||||
| for i in range(2): | ||||||
| if np.all(parity[:, i] == 0): | ||||||
| parity[:, [i, 2]] = parity[:, [2, i]] | ||||||
| break | ||||||
| gates: tuple[str, ...] = () | ||||||
| new: Union[_su2_ct.SU2CliffordT, None] | ||||||
| if np.array_equal(parity, np.array([[1, 1, 0], [1, 1, 0], [0, 0, 0]])): | ||||||
| # Leftmost syllabe is T | ||||||
| new = _su2_ct.Tz.adjoint() @ matrix | ||||||
| gates = ('T',) | ||||||
| elif np.array_equal(parity, np.array([[0, 0, 0], [1, 1, 0], [1, 1, 0]])): | ||||||
| # Leftmost syllabe is HT | ||||||
| new = _su2_ct.HSqrt2.adjoint() @ matrix | ||||||
| new = _su2_ct.Tz.adjoint() @ new | ||||||
| gates = ('T', 'H') | ||||||
| elif np.array_equal(parity, np.array([[1, 1, 0], [0, 0, 0], [1, 1, 0]])): | ||||||
| # Leftmost syllabe is SHT | ||||||
| new = _su2_ct.SSqrt2.adjoint() @ matrix | ||||||
| new = _su2_ct.HSqrt2.adjoint() @ new | ||||||
| new = _su2_ct.Tz.adjoint() @ new | ||||||
| gates = ('T', 'H', 'S') | ||||||
| else: | ||||||
| raise ValueError(f'Unexpected parity matrix:\n{parity}') | ||||||
| new = new.scale_down() | ||||||
| if new is None or not new.is_valid(): | ||||||
| raise ValueError('Invalid SU2CliffordT matrix') | ||||||
| seq = _matsumoto_amano_sequence(new) | ||||||
| return seq + gates | ||||||
|
|
||||||
|
|
||||||
| def to_sequence(matrix: _su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, ...]: | ||||||
| r"""Returns a sequence of Clifford+T that produces the given matrix. | ||||||
|
|
||||||
| Allowable format strings are | ||||||
| - 'xyz' uses Tx, Ty, Tz gates. | ||||||
| - 'xz' uses $Tx, Tz, Tx^\dagger, Tz^\dagger$ | ||||||
| - 't' uses T gates, and returns the Matsumoto-Amano normal form. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||
|
|
||||||
| Args: | ||||||
| matrix: The matrix to represent. | ||||||
| fmt: A string from the set {'xz', 'xyz'} representing the allowed T gates described above. | ||||||
| fmt: A string from the set {'xz', 'xyz', t'} representing the allowed T gates described above. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo:
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||
|
|
||||||
| Returns: | ||||||
| A tuple of strings representing the gates. | ||||||
|
|
@@ -100,6 +151,8 @@ def to_sequence(matrix: _su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, .. | |||||
| return _xyz_sequence(matrix) | ||||||
| if fmt == 'xz': | ||||||
| return cast(tuple[str, ...], _xz_sequence(matrix)) | ||||||
| if fmt == 't': | ||||||
| return _matsumoto_amano_sequence(matrix) | ||||||
| raise ValueError(f'{type=} is not supported') | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -116,6 +169,7 @@ def to_sequence(matrix: _su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, .. | |||||
| "Tx*": cirq.rx(-math.pi / 4), | ||||||
| "Ty*": cirq.ry(-math.pi / 4), | ||||||
| "Tz*": cirq.rz(-math.pi / 4), | ||||||
| "T": cirq.rz(math.pi / 4), | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -130,6 +184,8 @@ def _to_quirk_name(name: str, allow_global_phase: bool = False) -> str: | |||||
| if name == "S*": | ||||||
| return "\"Z^-½\"" | ||||||
| if name.startswith("T"): | ||||||
| if name == "T": | ||||||
| return "\"Z^¼\"" | ||||||
| if name.endswith("*"): | ||||||
| return "\"" + name[1].upper() + "^-¼" + "\"" | ||||||
| return "\"" + name[1].upper() + "^¼" + "\"" | ||||||
|
|
@@ -145,6 +201,8 @@ def _to_quirk_name(name: str, allow_global_phase: bool = False) -> str: | |||||
| if name == "S*": | ||||||
| return '{"id":"Rzft","arg":"-pi/2"}' | ||||||
| if name.startswith("T"): | ||||||
| if name == "T": | ||||||
| return '{"id":"Rzft","arg":"pi/4"}' | ||||||
| angle = ['pi/4', '-pi/4'][name.endswith('*')] | ||||||
| return '{"id":"R%sft","arg":"%s"}' % (name[1].lower(), angle) | ||||||
| raise ValueError(f"{name=} is not supported") | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -60,9 +60,47 @@ def test_to_xz_seq(g: _su2_ct.SU2CliffordT): | |||||
|
|
||||||
|
|
||||||
| @pytest.mark.parametrize("g", _make_random_su(50, 5, random_cliffords=True, seed=0)) | ||||||
| def test_to_cirq(g): | ||||||
| circuit = cirq.Circuit(ctr.to_cirq(g, 'xyz')) | ||||||
| @pytest.mark.parametrize("fmt", ('xyz', 't')) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets add xz as well
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||
| def test_to_cirq(g: _su2_ct.SU2CliffordT, fmt: str): | ||||||
| g = g.rescale() | ||||||
| circuit = cirq.Circuit(ctr.to_cirq(g, fmt)) | ||||||
| unitary = cirq.unitary(circuit) | ||||||
| u = g.matrix.astype(complex) | ||||||
| u = u / np.linalg.det(u) ** 0.5 | ||||||
| assert cirq.protocols.equal_up_to_global_phase(u, unitary) | ||||||
|
|
||||||
|
|
||||||
| @pytest.mark.parametrize( | ||||||
| ["g", "fmt", "expected"], | ||||||
| [ | ||||||
| [_su2_ct.HSqrt2, "t", ('"Z^½"', '"X"', '"Z^½"', '"X"', '"H"')], | ||||||
| [_su2_ct.SSqrt2, "t", ('{"id":"Rzft","arg":"pi/2"}',)], | ||||||
| [_su2_ct.Tz, "t", ('{"id":"Rzft","arg":"pi/4"}',)], | ||||||
| ], | ||||||
| ) | ||||||
| def test_to_quirk(g: _su2_ct.SU2CliffordT, fmt: str, expected: tuple[str, ...]): | ||||||
| assert ctr.to_quirk(g, fmt) == expected | ||||||
|
|
||||||
|
|
||||||
| @pytest.mark.parametrize("g", _make_random_su(50, 10, random_cliffords=True, seed=0)) | ||||||
| def test_to_matsumoto_amano_seq(g: _su2_ct.SU2CliffordT): | ||||||
| g = g.rescale() | ||||||
| seq = ctr.to_sequence(g, 't') | ||||||
| prev_t = -1 | ||||||
| # Check that the reversed list matches the regular expression $(T|\eps)(HT|SHT)^*C$. | ||||||
| seq_r = list(reversed(seq)) | ||||||
| for i in range(len(seq_r)): | ||||||
| assert seq_r[i] in ('T', 'H', 'S', 'Z', 'X') | ||||||
| if i == 0 and seq_r[0] == 'T': | ||||||
| prev_t = 0 | ||||||
| continue | ||||||
| if seq_r[i] == 'T': | ||||||
| # Check that this is one of HT, SHT syllabes | ||||||
| assert prev_t in (i - 2, i - 3) | ||||||
| if prev_t == i - 2: | ||||||
| assert seq_r[i - 1] == 'H' | ||||||
| else: | ||||||
| assert seq_r[i - 2] + seq_r[i - 1] == 'SH' | ||||||
| prev_t = i | ||||||
| got = _su2_ct.SU2CliffordT.from_sequence(seq) | ||||||
| assert got == g | ||||||
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.
can you move this if/else block into a separate method and turn _matsumoto_amano_sequence into an iterative procedure rather than recursive?
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.
Done.