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

Added trim_qubits option to Circuit.split() #353

Merged
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
12 changes: 8 additions & 4 deletions tangelo/linq/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,13 @@ def get_entangled_indices(self):

return entangled_indices

def split(self):
def split(self, trim_qubits=True):
""" Split a circuit featuring unentangled qubit subsets into as many circuit objects.
Each circuit only contains the gate operations targeting the qubit indices in its subsets.

Args:
trim_qubits (bool): Trim qubits on each circuit object and reindex to lowest value, Default: True

Returns:
list of Circuit: list of resulting circuits
"""
Expand All @@ -308,9 +311,10 @@ def split(self):
separate_circuits[i].add_gate(g)
break

# Trim unnecessary indices in the new circuits
for c in separate_circuits:
c.trim_qubits()
if trim_qubits:
# Trim unnecessary indices in the new circuits
for c in separate_circuits:
c.trim_qubits()
return separate_circuits

def stack(self, *other_circuits):
Expand Down
2 changes: 1 addition & 1 deletion tangelo/linq/qpu_connection/ibm_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def job_submit(self, program, backend_name, n_shots, circuits, operators=None, r
circuits (Circuit | List[Circuit]): Tangelo circuit(s)
operators (QubitOperator | List[QubitOperator]) : Optional, qubit operators for computing expectation values
runtime_options (dict): Optional, extra keyword arguments for options supported in qiskit-runtime.
instance (str): Optional, desired IBM service instance in the "hub/group/project" format. Default is likely to send to "ibm-q/open/main"
instance (str): Optional, desired IBM service instance in the "hub/group/project" format. Default is likely to send to "ibm-q/open/main"

Returns:
str: string representing the job id
Expand Down
9 changes: 9 additions & 0 deletions tangelo/linq/tests/test_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ def test_split_circuit(self):
self.assertTrue(c2 == Circuit([Gate("CSWAP", target=[0, 2], control=[1])]))
self.assertTrue(c3 == Circuit([Gate("H", target=0)]))

c = Circuit([Gate("CSWAP", target=[2, 5], control=[0]),
Gate("CSWAP", target=[3, 7], control=[4]),
Gate("H", 6)])
c1, c2, c3 = c.split(trim_qubits=False)

self.assertTrue(c1 == Circuit([Gate("CSWAP", target=[2, 5], control=[0])]))
self.assertTrue(c2 == Circuit([Gate("CSWAP", target=[3, 7], control=[4])]))
self.assertTrue(c3 == Circuit([Gate("H", target=6)]))

def test_stack_circuits(self):
""" Test circuit stacking """

Expand Down
10 changes: 10 additions & 0 deletions tangelo/toolboxes/operators/tests/test_trim_trivial_qubits.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ def test_trim_trivial_qubits(self):
self.assertEqual(ref_circ._gates, trimmed_circuit._gates)
self.assertAlmostEqual(sim.get_expectation_value(trimmed_operator, trimmed_circuit), ref_value+1, places=5)

def test_trim_trivial_qubits_unentangled_non_trivial_pieces(self):
""" Test if trim trivial qubit function produces correct and compatible circuits and operators """

unentangled_circ = Circuit([Gate("RY", 0, parameter=0.1), Gate("RY", 3, parameter=0.2)])
qu_op = QubitOperator("Z0 Z1 Z3")
ref_exp = sim.get_expectation_value(qu_op, unentangled_circ)

trimmed_operator, trimmed_circuit = trim_trivial_qubits(qu_op, unentangled_circ)
self.assertAlmostEqual(sim.get_expectation_value(trimmed_operator, trimmed_circuit), ref_exp, places=5)


if __name__ == "__main__":
unittest.main()
20 changes: 13 additions & 7 deletions tangelo/toolboxes/operators/trim_trivial_qubits.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Dict, Tuple, Union

import numpy as np

from tangelo.toolboxes.operators import QubitOperator, count_qubits
from tangelo.linq import Circuit
from tangelo.linq import Circuit, Gate
from tangelo.linq.helpers.circuits import pauli_string_to_of, pauli_of_to_string


def trim_trivial_operator(qu_op, trim_states, n_qubits=None, reindex=True):
def trim_trivial_operator(qu_op: QubitOperator, trim_states: Dict[int, int],
n_qubits: Union[None, int] = None, reindex: bool = True) -> QubitOperator:
"""
Calculate expectation values of a QubitOperator acting on qubits in a
trivial |0> or |1> state. Return a trimmed QubitOperator with updated coefficients
Expand Down Expand Up @@ -58,7 +61,7 @@ def trim_trivial_operator(qu_op, trim_states, n_qubits=None, reindex=True):
return qu_op_trim


def is_bitflip_gate(gate, atol=1e-5):
def is_bitflip_gate(gate: Gate, atol: float = 1e-5) -> bool:
"""
Check if a gate is a bitflip gate.

Expand Down Expand Up @@ -90,7 +93,7 @@ def is_bitflip_gate(gate, atol=1e-5):
return False


def trim_trivial_circuit(circuit):
def trim_trivial_circuit(circuit: Circuit) -> Tuple[Circuit, Dict[int, int]]:
"""
Split Circuit into entangled and unentangled components.
Returns entangled Circuit, and the indices and states of unentangled qubits
Expand All @@ -103,7 +106,7 @@ def trim_trivial_circuit(circuit):
dict : dictionary mapping trimmed qubit indices to their states (0 or 1)
"""
# Split circuit and get relevant indices
circs = circuit.split()
circs = circuit.split(trim_qubits=False)
e_indices = circuit.get_entangled_indices()
used_qubits = set()
for eq in e_indices:
Expand All @@ -117,7 +120,8 @@ def trim_trivial_circuit(circuit):
circuit_new = Circuit()
# Go through circuit components, trim if trivial, otherwise append to new circuit
for i, circ in enumerate(circs):
if circ.width != 1 or circ.size not in (1, 2):
circ_width = len(circ._qubit_indices)
if circ_width != 1 or circ.size not in (1, 2):
circuit_new += circ
continue

Expand Down Expand Up @@ -155,10 +159,12 @@ def trim_trivial_circuit(circuit):
else:
circuit_new += circ

circuit_new.trim_qubits()

return circuit_new, dict(sorted(trim_states.items()))


def trim_trivial_qubits(operator, circuit):
def trim_trivial_qubits(operator: QubitOperator, circuit: Circuit) -> Tuple[QubitOperator, Circuit]:
"""
Trim circuit and operator based on expectation values calculated from
trivial components of the circuit.
Expand Down
Loading