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

Symmetry post-selection #232

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions tangelo/toolboxes/post_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
from .histogram import Histogram, aggregate_histograms, filter_hist
from .mc_weeny_rdm_purification import mcweeny_purify_2rdm
from .extrapolation import diis, richardson
from .post_selection import ancilla_symmetry_circuit, post_select, strip_post_selection
2 changes: 1 addition & 1 deletion tangelo/toolboxes/post_processing/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Histogram:
Args:
outcomes (dict of string: int or float): Results in the format of bitstring:
outcome, where outcome can be a number of shots a probability.
n_shots (int): Self-explanatory. If it is greater than 0, the class
n_shots (int): Self-explanatory. If it is equal 0, the class
considers that probabilities are provided.
msq_first (bool): Bit ordering. For example, 011 (msq_first) = 110
(lsq_first). This depends on the hardware provider convention.
Expand Down
96 changes: 96 additions & 0 deletions tangelo/toolboxes/post_processing/post_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright 2022 Good Chemistry Company.
#
# 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
#
# http://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.

"""This module provides functions to create symmetry verification and post-selection circuits."""

import warnings

from tangelo.linq import Circuit, Gate
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
from tangelo.linq.helpers import measurement_basis_gates, pauli_string_to_of
from tangelo.toolboxes.operators import QubitOperator, count_qubits
from tangelo.toolboxes.post_processing import Histogram


def ancilla_symmetry_circuit(circuit, sym_op):
"""Append a symmetry operator circuit to an input circuit using an extra ancilla qubit,
for the purpose of symmetry verification and post-selection.
For more details see arXiv:1807.10050.

Args:
circuit (Circuit): A quantum circuit to be equipped with symmetry verification
sym_op (string): The symmetry operator to be verified.
Can be a Pauli string, OpenFermion style list or a QubitOperator

Returns:
Circuit: The input circuit appended with the proper basis rotation
and entanglement with an ancilla qubit, which is added as the last qubit.
Increases the input circuit width by 1."""
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(sym_op, QubitOperator):
op_len = count_qubits(sym_op)
else:
op_len = len(sym_op)
n_qubits = circuit.width

# Check if the operator size matches the circuit width
if n_qubits < op_len:
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
raise RuntimeError("The size of the symmetry operator is bigger than the circuit width.")
elif n_qubits > op_len:
warnings.warn("The size of the symmetry operator is smaller than the circuit width. Remaining qubits will be measured in the Z-basis.")

if isinstance(sym_op, str):
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
basis_gates = measurement_basis_gates(pauli_string_to_of(sym_op))
elif isinstance(sym_op, list or tuple):
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
basis_gates = measurement_basis_gates(sym_op)
elif isinstance(sym_op, QubitOperator):
basis_gates = measurement_basis_gates(list(sym_op.terms.keys())[0])

basis_circ = Circuit(basis_gates)
parity_gates = [Gate("CNOT", n_qubits, i) for i in range(n_qubits)]
KrzysztofB-1qbit marked this conversation as resolved.
Show resolved Hide resolved
circuit_new = circuit + basis_circ + Circuit(parity_gates) + basis_circ.inverse()
ValentinS4t1qbit marked this conversation as resolved.
Show resolved Hide resolved
return circuit_new


def post_select(freqs, expected_outcomes):
"""Apply post selection to frequency data based on
a dictionary of expected outcomes on ancilla qubits.

Args:
freqs (dict): A dictionary of {bitstring: frequency} pairs
expected_outcomes (dict): Desired outcomes on certain qubit indices
and their expected state. For example, {0: "1", 1: "0"} would
filter results based on the first qubit with the |1> state and
the second qubit with the |0> state measured.

Returns:
dict: A dictionary of post-selected, renormalized frequencies
and bitstrings with removed ancilla qubits
"""
hist = Histogram(freqs, n_shots=0)
hist.post_select(expected_outcomes)
return hist.frequencies


def strip_post_selection(freqs, *qubits):
"""Convenience function to remove the symmetry ancilla qubit
and aggregate data to recreate results without post-selection.

Args:
freqs (dict): A dictionary of {bitstring: frequency} as returned from a quantum device
qubits (variable number of int): The ancilla qubit indices to be removed from the bitstrings

Returns:
dict: A frequency dictionary with the qubits stripped and data aggregated"""
hist = Histogram(freqs, n_shots=0)
hist.remove_qubit_indices(*qubits)
return hist.frequencies
60 changes: 60 additions & 0 deletions tangelo/toolboxes/post_processing/tests/test_post_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2022 Good Chemistry Company.
#
# 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
#
# http://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 unittest

from tangelo.linq import Circuit, Gate
from tangelo.toolboxes.post_processing import post_select, strip_post_selection, ancilla_symmetry_circuit

circ = Circuit([Gate("H", 0), Gate("CNOT", 1, 0)])

sym_circ = Circuit([Gate("H", 0), Gate("CNOT", 1, 0),
Gate('RY', 0, parameter=-1.5707963267948966),
Gate("CNOT", 2, 0), Gate("CNOT", 2, 1),
Gate('RY', 0, parameter=1.5707963267948966)])

hist = {"000": 0.0087, "001": 0.0003, "010": 0.0056, "011": 0.0481,
"100": 0.0053, "101": 0.0035, "110": 0.9136, "111": 0.0149}


class PostSelectionTest(unittest.TestCase):

def test_symmetry_circuit(self):
"""Test the ancilla symmetry circuit constructor"""
with self.assertRaises(RuntimeError):
ancilla_symmetry_circuit(circ, "XYZ")

with self.assertWarns(UserWarning):
test_circ = ancilla_symmetry_circuit(circ, "X")

self.assertEqual(sym_circ, test_circ)

def test_post_select(self):
"""Test equality of post-selected frequencies with reference values rounded to 1e-4"""
hist_ref = {'00': 0.0093, '01': 0.0060, '10': 0.0057, '11': 0.9790}
hist_post = post_select(hist, {2: "0"})

for key, value in hist_ref.items():
self.assertAlmostEqual(value, hist_post[key], delta=1e-4)

def test_strip_post_selection(self):
"""Test stripping of ancilla and aggregation of corresponding frequencies"""
hist_ref = {"00": 0.0090, "01": 0.0537, "10": 0.0088, "11": 0.9285}
hist_strip = strip_post_selection(hist, 2)

self.assertDictEqual(hist_ref, hist_strip)


if __name__ == "__main__":
unittest.main()