From ec7d26ad6c89d1cb1645dc697f49aa6c181a5d2e Mon Sep 17 00:00:00 2001 From: raksharuia Date: Thu, 17 Oct 2024 18:10:44 +0200 Subject: [PATCH 1/3] working implementation of labelled cnots in qiskit converter --- perceval/converters/abstract_converter.py | 31 ++++- perceval/converters/qiskit_converter.py | 21 +++- perceval/utils/converters.py | 132 ++++++++++++++++++++++ tests/test_converter_qiskit.py | 32 ++++++ 4 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 perceval/utils/converters.py diff --git a/perceval/converters/abstract_converter.py b/perceval/converters/abstract_converter.py index e75d72c3..64c7e3fa 100644 --- a/perceval/converters/abstract_converter.py +++ b/perceval/converters/abstract_converter.py @@ -30,7 +30,7 @@ from abc import ABC, abstractmethod from perceval.components import Port, Circuit, Processor, Source, catalog -from perceval.utils import P, BasicState, Encoding, global_params +from perceval.utils import P, BasicState, Encoding, global_params, PostSelect from perceval.utils.algorithms.optimize import optimize from perceval.utils.algorithms.norm import frobenius import perceval.components.unitary_components as comp @@ -131,14 +131,37 @@ def _create_2_qubit_gates_from_catalog( CRz - Heralded and post-processed (uses two CNOTs) SWAP """ + + # save the current post-selection information from the converted + # processor before adding the next gate + if self._converted_processor._postselect is not None: + post_select_curr = self._converted_processor._postselect + else: + post_select_curr = PostSelect() # save empty if I need to merge incoming PostSelect to it + + self._converted_processor.clear_postselection() # clear current post-selection + gate_name = gate_name.upper() - if gate_name in ["CNOT", "CX"]: + if gate_name in ["CNOT", "CX", "CX:RALPH", "CX:KNILL"]: self._cnot_idx += 1 - if use_postselection and self._cnot_idx == n_cnot: + # if use_postselection and self._cnot_idx == n_cnot: + # cnot_processor = self.create_ppcnot_processor() + # else: + # cnot_processor = self.create_hcnot_processor() + if use_postselection and gate_name == "CX:RALPH": cnot_processor = self.create_ppcnot_processor() + cnot_ps = cnot_processor._postselect + + cnot_processor.clear_postselection() # clear after saving post select information + post_select_curr.merge(cnot_ps) # merge the incoming gate post-selection with the current + #elif gate_name == "CX:KNILL": else: cnot_processor = self.create_hcnot_processor() + # else: + # raise ValueError(f'Invalid CNOT type gate {gate_name}') + self._converted_processor.add(_create_mode_map(c_idx, c_data), cnot_processor) + elif gate_name in ["CSIGN", "CZ"]: # Controlled Z in myqlm is named CSIGN cz_processor = self.create_hcz_processor() @@ -157,4 +180,6 @@ def _create_2_qubit_gates_from_catalog( else: raise UnknownGateError(f"Gate not yet supported: {gate_name}") + # re-apply the cleared post-selection + self._converted_processor.set_postselection(post_select_curr) return self._converted_processor diff --git a/perceval/converters/qiskit_converter.py b/perceval/converters/qiskit_converter.py index 6080b4f5..2f744dba 100644 --- a/perceval/converters/qiskit_converter.py +++ b/perceval/converters/qiskit_converter.py @@ -29,7 +29,19 @@ from perceval.components import Processor, Source from perceval.utils.logging import get_logger, channel +from perceval.utils.converters import _label_cnots_in_gate_sequence from .abstract_converter import AGateConverter +from .circuit_to_graph_converter import gates_and_qubits + +def _get_gate_sequence(qisk_circ) -> list: + # returns a nested list of gate names with corresponding qubit positions + gate_names, qubit_pos = gates_and_qubits(qisk_circ) # from qiskit circuit + + gate_info = [] + for index, elem in enumerate(gate_names): + gate_info.append([gate_names[index], qubit_pos[index]]) + + return gate_info class QiskitConverter(AGateConverter): @@ -58,6 +70,10 @@ def convert(self, qc, use_postselection: bool = True) -> Processor: get_logger().info(f"Convert qiskit.QuantumCircuit ({qc.num_qubits} qubits, {len(qc.data)} operations) to processor", channel.general) + gate_sequence = _get_gate_sequence(qc) + optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) + print('optimised cnot gate info list', optimized_gate_sequence) + n_cnot = 0 # count the number of CNOT gates in circuit - needed to find the num. heralds for instruction in qc.data: if instruction[0].name == "cx": @@ -66,7 +82,7 @@ def convert(self, qc, use_postselection: bool = True) -> Processor: qubit_names = qc.qregs[0].name self._configure_processor(qc, qname=qubit_names) # empty processor with ports initialized - for instruction in qc.data: + for gate_index, instruction in enumerate(qc.data): # barrier has no effect if isinstance(instruction[0], qiskit.circuit.barrier.Barrier): continue @@ -85,7 +101,8 @@ def convert(self, qc, use_postselection: bool = True) -> Processor: c_idx = qc.find_bit(instruction[1][0])[0] * 2 c_data = qc.find_bit(instruction[1][1])[0] * 2 self._create_2_qubit_gates_from_catalog( - instruction[0].name, + # instruction[0].name, + optimized_gate_sequence[gate_index], n_cnot, c_idx, c_data, diff --git a/perceval/utils/converters.py b/perceval/utils/converters.py new file mode 100644 index 00000000..97487e97 --- /dev/null +++ b/perceval/utils/converters.py @@ -0,0 +1,132 @@ +# MIT License +# +# Copyright (c) 2022 Quandela +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# As a special exception, the copyright holders of exqalibur library give you +# permission to combine exqalibur with code included in the standard release of +# Perceval under the MIT license (or modified versions of such code). You may +# copy and distribute such a combined system following the terms of the MIT +# license for both exqalibur and Perceval. This exception for the usage of +# exqalibur is limited to the python bindings used by Perceval. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from itertools import combinations + + +def _is_cyclic_util(v: int, visited: list, parent: int, adj_list: list) -> bool: + visited[v] = True + for i in adj_list[v]: + if not visited[i]: + if _is_cyclic_util(i, visited, v, adj_list): + return True + elif parent != i: + return True + return False + +def _is_cyclic(adj_list: list, cnot_node_count: int) -> bool: + # returns True if a cyclic pair is found + visited = [False] * cnot_node_count + for i in range(cnot_node_count): + if not visited[i]: + if _is_cyclic_util(i, visited, -1, adj_list): + return True + return False + +def _find_max_ralph_pairs(pairs) -> list: + # finds largest set of acyclic edges -> Ralph CNOTs can be added to these positions + nodes = set() # create a set of all positions (mode indices) at which CNOT exists + for pair in pairs: + nodes.update(pair) + + node_map = {node: idx for idx, node in enumerate(nodes)} + cnot_node_count = len(nodes) + + max_subset = [] # list of pairs of acyclic combinations of modes + + # Check all possible subsets of the pairs list + for r in range(1, len(pairs) + 1): + for subset in combinations(pairs, r): + # Build adjacency list for this subset + adj_list = [[] for _ in range(cnot_node_count)] + for u, v in subset: + adj_list[node_map[u]].append(node_map[v]) + adj_list[node_map[v]].append(node_map[u]) + + # Check if this subset forms a cycle + if not _is_cyclic(adj_list, cnot_node_count): + if len(subset) > len(max_subset): + max_subset = subset + + return list(max_subset) + + +def _gate_list_optimized_cnots(gate_info: list) -> list: + """ + Optimizes the placement of CNOT gates within the converted circuit. + Extracts relevant information from the gate sequency to decide where + to insert Ralph or Knill CNOTs + + :param gate_info: list of gate sequences with corresponding gate names and positions + :return: list of labelled CNOTs in sequential order + """ + + cnot_list_in_order = [elem for elem in gate_info if elem[0] == 'cx'] # extracts CNOTs in sequence from gate list + cnot_pos_pairs = [elem[1] for elem in cnot_list_in_order] # list of CNOT qubit pos pairs in order of appearance + + ralph_pairs_list = _find_max_ralph_pairs(cnot_pos_pairs[::-1]) + + # CNOT + cnot_type_list = [] + + # This for loop generates the list for cnot_types - Knill or Ralph + for pair in cnot_pos_pairs[::-1]: + if pair in ralph_pairs_list: + cnot_type_list.append('cx:Ralph') + ralph_pairs_list.remove(pair) + else: + cnot_type_list.append('cx:Knill') + + cnot_type_list.reverse() # required as it was created by going through CNOTs in reverse + + cnot_order_named = [] # List of named CNOTs to be im[;emented in converted circuit + for index, cnot_elem in enumerate(cnot_list_in_order): + cnot_order_named.append([cnot_type_list[index], cnot_elem[1]]) # cnot_elem[1] -> qubit positions + + return cnot_order_named + + +def _label_cnots_in_gate_sequence(gate_info: list) -> list: + """ + Processes a list of gate sequence to return an optimized list of gate names. + During the conversion from a gate-based circuit to a linear optical circuit, + CNOTs need to be classified as either Ralph or Knill for optimized use of + resources in the circuit. + + :param gate_info: list of gate sequences with corresponding gate names and positions + """ + cnot_order_named = _gate_list_optimized_cnots(gate_info) + # generate gate info with CNOT names + cnot_counter = 0 + for i, elem in enumerate(gate_info): + if elem[0] == 'cx': + gate_info[i] = cnot_order_named[cnot_counter] + cnot_counter += 1 + + return [elem[0] for elem in gate_info] # extract list of gate names diff --git a/tests/test_converter_qiskit.py b/tests/test_converter_qiskit.py index bdfe8ab5..ff967913 100644 --- a/tests/test_converter_qiskit.py +++ b/tests/test_converter_qiskit.py @@ -28,6 +28,7 @@ # SOFTWARE. import pytest +import numpy as np try: import qiskit @@ -206,3 +207,34 @@ def test_cnot_herald(): assert bsd_out[BasicState("|1,0,0,1>")] + bsd_out[BasicState("|0,1,1,0>")] < 2e-5 assert bsd_out[BasicState("|1,0,1,0>")] + bsd_out[BasicState("|0,1,0,1>")] > 0.99 assert len(bsd_out) == 4 + +def qiskit_circ_multiple_cnots(): + # Gate Circuit + params = np.random.random(8) + circ = qiskit.QuantumCircuit(4) + + circ.rx(params[0], 0) + circ.ry(params[1], 1) + circ.rx(params[2], 2) + circ.rx(params[3], 3) + + circ.cx(1, 2) + + circ.rx(params[4], 0) + circ.ry(params[5], 1) + circ.rx(params[6], 2) + circ.rx(params[7], 3) + + circ.cx(0, 1) + circ.cx(0, 1) + circ.cx(2, 3) + circ.cx(2, 3) + return circ + +def test_cnot_ralph_vs_knill(): + qisk_circ = qiskit_circ_multiple_cnots() + converter = QiskitConverter() + pc = converter.convert(qisk_circ) + import perceval as pcvl + pcvl.pdisplay(pc) + # todo : add assertions From 0076b24e9041b8662773d451263d609c119dbc8d Mon Sep 17 00:00:00 2001 From: raksharuia Date: Fri, 18 Oct 2024 14:04:08 +0200 Subject: [PATCH 2/3] modify myqlm converter with ralph cnots --- perceval/converters/myqlm_converter.py | 21 +++++++++++++++------ perceval/utils/converters.py | 9 +++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/perceval/converters/myqlm_converter.py b/perceval/converters/myqlm_converter.py index 6134436b..4f0fd1d4 100644 --- a/perceval/converters/myqlm_converter.py +++ b/perceval/converters/myqlm_converter.py @@ -29,9 +29,19 @@ from perceval.components import Circuit, Processor, Source, BS, PS from perceval.utils.logging import get_logger, channel +from perceval.utils.converters import _label_cnots_in_gate_sequence from .abstract_converter import AGateConverter +def _get_gate_sequence(myqlm_circ) -> list: + # returns a nested list of gate names with corresponding qubit positions from a myqlm circuit + gate_info = [] + for gate_instruction in myqlm_circ.iterate_simple(): + gate_info.append([gate_instruction[0], gate_instruction[2]]) + + return gate_info + + class MyQLMConverter(AGateConverter): r"""myQLM quantum circuit to perceval circuit converter. @@ -60,6 +70,10 @@ def convert(self, qlmc, use_postselection: bool = True) -> Processor: # this nested import fixes automatic class reference generation get_logger().info(f"Convert myQLM circuit ({qlmc.nbqbits} qubits) to processor", channel.general) + + gate_sequence = _get_gate_sequence(qlmc) + optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) + n_cnot = qlmc.count("CNOT") # count the number of CNOT gates in circuit - needed to find the num. heralds self._configure_processor(qlmc) # empty processor with ports initialized @@ -94,12 +108,7 @@ def convert(self, qlmc, use_postselection: bool = True) -> Processor: raise ValueError(f"Gates with number of Qbits higher than 2 not implemented") c_idx = instruction_qbit[0] * 2 c_data = instruction_qbit[1] * 2 - self._create_2_qubit_gates_from_catalog( - instruction_name, - n_cnot, - c_idx, - c_data, - use_postselection) + self._create_2_qubit_gates_from_catalog(optimized_gate_sequence[i], c_idx, c_data, use_postselection) self.apply_input_state() return self._converted_processor diff --git a/perceval/utils/converters.py b/perceval/utils/converters.py index 97487e97..75a216b2 100644 --- a/perceval/utils/converters.py +++ b/perceval/utils/converters.py @@ -29,6 +29,7 @@ from itertools import combinations +CNOT_NAMES = ['CX', 'CNOT'] def _is_cyclic_util(v: int, visited: list, parent: int, adj_list: list) -> bool: visited[v] = True @@ -87,7 +88,7 @@ def _gate_list_optimized_cnots(gate_info: list) -> list: :return: list of labelled CNOTs in sequential order """ - cnot_list_in_order = [elem for elem in gate_info if elem[0] == 'cx'] # extracts CNOTs in sequence from gate list + cnot_list_in_order = [elem for elem in gate_info if elem[0].upper() in CNOT_NAMES] # extracts CNOTs in sequence from gate list cnot_pos_pairs = [elem[1] for elem in cnot_list_in_order] # list of CNOT qubit pos pairs in order of appearance ralph_pairs_list = _find_max_ralph_pairs(cnot_pos_pairs[::-1]) @@ -98,10 +99,10 @@ def _gate_list_optimized_cnots(gate_info: list) -> list: # This for loop generates the list for cnot_types - Knill or Ralph for pair in cnot_pos_pairs[::-1]: if pair in ralph_pairs_list: - cnot_type_list.append('cx:Ralph') + cnot_type_list.append('CX:RALPH') ralph_pairs_list.remove(pair) else: - cnot_type_list.append('cx:Knill') + cnot_type_list.append('CX:KNILL') cnot_type_list.reverse() # required as it was created by going through CNOTs in reverse @@ -125,7 +126,7 @@ def _label_cnots_in_gate_sequence(gate_info: list) -> list: # generate gate info with CNOT names cnot_counter = 0 for i, elem in enumerate(gate_info): - if elem[0] == 'cx': + if elem[0].upper() in CNOT_NAMES: gate_info[i] = cnot_order_named[cnot_counter] cnot_counter += 1 From b2053268d401275e9fb531a93e8fb0091f37484a Mon Sep 17 00:00:00 2001 From: raksharuia Date: Fri, 18 Oct 2024 16:13:03 +0200 Subject: [PATCH 3/3] modify all converters to use ralph cnot with Liams rules --- perceval/converters/abstract_converter.py | 31 +++++---------------- perceval/converters/cqasm_converter.py | 33 +++++++++++++---------- perceval/converters/myqlm_converter.py | 1 - perceval/converters/qiskit_converter.py | 15 ++--------- tests/test_converter_qiskit.py | 22 ++++++++++++--- 5 files changed, 46 insertions(+), 56 deletions(-) diff --git a/perceval/converters/abstract_converter.py b/perceval/converters/abstract_converter.py index 64c7e3fa..295f007c 100644 --- a/perceval/converters/abstract_converter.py +++ b/perceval/converters/abstract_converter.py @@ -30,7 +30,7 @@ from abc import ABC, abstractmethod from perceval.components import Port, Circuit, Processor, Source, catalog -from perceval.utils import P, BasicState, Encoding, global_params, PostSelect +from perceval.utils import P, BasicState, Encoding, global_params, PostSelect, NoiseModel from perceval.utils.algorithms.optimize import optimize from perceval.utils.algorithms.norm import frobenius import perceval.components.unitary_components as comp @@ -52,8 +52,7 @@ class AGateConverter(ABC): def __init__(self, backend_name: str = "SLOS", source: Source = Source()): self._converted_processor = None self._input_list = None # input state in list - self._cnot_idx = 0 # counter for CNOTS in circuit - self._source = source + self._noise_model = NoiseModel() self._backend_name = backend_name # Define function handler to create complex components @@ -82,7 +81,7 @@ def _configure_processor(self, gate_circuit, **kwargs): n_moi = n_qbits * 2 # In dual rail, number of modes of interest = 2 * number of qbits self._input_list = [0] * n_moi - self._converted_processor = Processor(self._backend_name, n_moi, self._source) + self._converted_processor = Processor(self._backend_name, n_moi, noise=self._noise_model) for i in range(n_qbits): self._converted_processor.add_port(i * 2, Port(Encoding.DUAL_RAIL, qubit_names[i])) self._input_list[i * 2] = 1 @@ -116,14 +115,7 @@ def _create_generic_1_qubit_gate(self, u) -> Circuit: optimize(ins, u, frobenius, sign=-1) return ins - def _create_2_qubit_gates_from_catalog( - self, - gate_name: str, - n_cnot, - c_idx, - c_data, - use_postselection, - parameter=None): + def _create_2_qubit_gates_from_catalog(self, gate_name: str, c_idx: int, c_data: int, use_postselection: bool): r""" List of Gates implemented: CNOT - Heralded and post-processed @@ -131,34 +123,23 @@ def _create_2_qubit_gates_from_catalog( CRz - Heralded and post-processed (uses two CNOTs) SWAP """ - - # save the current post-selection information from the converted - # processor before adding the next gate + # Save and clear current post-selection data from the converted processor before adding the next gate if self._converted_processor._postselect is not None: post_select_curr = self._converted_processor._postselect else: post_select_curr = PostSelect() # save empty if I need to merge incoming PostSelect to it - self._converted_processor.clear_postselection() # clear current post-selection gate_name = gate_name.upper() - if gate_name in ["CNOT", "CX", "CX:RALPH", "CX:KNILL"]: - self._cnot_idx += 1 - # if use_postselection and self._cnot_idx == n_cnot: - # cnot_processor = self.create_ppcnot_processor() - # else: - # cnot_processor = self.create_hcnot_processor() + if gate_name in ["CX:RALPH", "CX:KNILL"]: if use_postselection and gate_name == "CX:RALPH": cnot_processor = self.create_ppcnot_processor() cnot_ps = cnot_processor._postselect cnot_processor.clear_postselection() # clear after saving post select information post_select_curr.merge(cnot_ps) # merge the incoming gate post-selection with the current - #elif gate_name == "CX:KNILL": else: cnot_processor = self.create_hcnot_processor() - # else: - # raise ValueError(f'Invalid CNOT type gate {gate_name}') self._converted_processor.add(_create_mode_map(c_idx, c_data), cnot_processor) diff --git a/perceval/converters/cqasm_converter.py b/perceval/converters/cqasm_converter.py index 1e6d5012..28f1b788 100644 --- a/perceval/converters/cqasm_converter.py +++ b/perceval/converters/cqasm_converter.py @@ -27,6 +27,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from perceval.components import Processor, Source, Circuit, BS, PS, PERM +from perceval.utils.converters import _label_cnots_in_gate_sequence from perceval.utils.logging import get_logger, channel from .abstract_converter import AGateConverter @@ -66,7 +67,6 @@ "CNOT", "CZ" } - def _cs(s: str) -> str: r"""A shortcut to clean up the strings generated by the qASM parser""" return s[2:-1] @@ -96,7 +96,6 @@ def __init__(self, backend_name: str = "SLOS", source: Source = Source()): import cqasm.v3x as cqasm self._qubit_list = [] - self._num_cnots = 0 self._use_postselection = False self._cqasm = cqasm @@ -124,7 +123,7 @@ def _operand_to_qubit_indices(self, operand): else: raise ConversionUnsupportedFeatureError(f"Cannot map variable { name } to a declared qubit") - def _convert_statement(self, statement): + def _get_gate_inf(self, statement): gate_name = statement.name num_operands = len(statement.operands) @@ -155,6 +154,11 @@ def _convert_statement(self, statement): raise ConversionUnsupportedFeatureError( f"Gate { gate_name } has more than one control (n = { num_controls })") + return gate_name, controls, targets, parameter + + def _convert_statement(self, statement, gate_index, optimized_gate_sequence): + gate_name, controls, targets, parameter = self._get_gate_inf(statement) + if not controls: circuit_template = _CQASM_1_QUBIT_GATES.get(gate_name, None) if not circuit_template: @@ -170,13 +174,8 @@ def _convert_statement(self, statement): raise ConversionUnsupportedFeatureError( f"Unsupported 2-qubit gate { gate_name }") for target in targets: - self._create_2_qubit_gates_from_catalog( - gate_name, - self._num_cnots, - controls[0] * 2, - target * 2, - self._use_postselection, - parameter=parameter) + self._create_2_qubit_gates_from_catalog(optimized_gate_sequence[gate_index], controls[0] * 2, + target * 2, self._use_postselection) def convert(self, ast, use_postselection: bool = True) -> Processor: r"""Convert a cQASM quantum program into a `Processor`. @@ -193,16 +192,22 @@ def convert(self, ast, use_postselection: bool = True) -> Processor: get_logger().info(f"Convert cqasm.ast ({len(self._qubit_list)} qubits, {len(ast.block.statements)} operations) to processor", channel.general) self._collect_qubit_list(ast) - self._num_cnots = sum( - (s.name == "CNOT") + 2 * (s.name in ["CR", "CRk"]) - for s in ast.block.statements) self._use_postselection = use_postselection qubit_names = [ f'{ q }[{ i }]' if i >= 0 else q for (q, i) in self._qubit_list] self._configure_processor(ast, qubit_names=qubit_names) + + # for gate sequence to optimize cnots + gate_sequence = [] for statement in ast.block.statements: - self._convert_statement(statement) + gate_name, controls, targets, parameter = self._get_gate_inf(statement) + gate_sequence.append([gate_name, controls + targets]) + + optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) + + for gate_index, statement in enumerate(ast.block.statements): + self._convert_statement(statement, gate_index, optimized_gate_sequence) self.apply_input_state() return self._converted_processor diff --git a/perceval/converters/myqlm_converter.py b/perceval/converters/myqlm_converter.py index 4f0fd1d4..d61b1eba 100644 --- a/perceval/converters/myqlm_converter.py +++ b/perceval/converters/myqlm_converter.py @@ -74,7 +74,6 @@ def convert(self, qlmc, use_postselection: bool = True) -> Processor: gate_sequence = _get_gate_sequence(qlmc) optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) - n_cnot = qlmc.count("CNOT") # count the number of CNOT gates in circuit - needed to find the num. heralds self._configure_processor(qlmc) # empty processor with ports initialized for i, instruction in enumerate(qlmc.iterate_simple()): diff --git a/perceval/converters/qiskit_converter.py b/perceval/converters/qiskit_converter.py index 2f744dba..affb1c43 100644 --- a/perceval/converters/qiskit_converter.py +++ b/perceval/converters/qiskit_converter.py @@ -72,12 +72,6 @@ def convert(self, qc, use_postselection: bool = True) -> Processor: gate_sequence = _get_gate_sequence(qc) optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) - print('optimised cnot gate info list', optimized_gate_sequence) - - n_cnot = 0 # count the number of CNOT gates in circuit - needed to find the num. heralds - for instruction in qc.data: - if instruction[0].name == "cx": - n_cnot += 1 qubit_names = qc.qregs[0].name self._configure_processor(qc, qname=qubit_names) # empty processor with ports initialized @@ -100,12 +94,7 @@ def convert(self, qc, use_postselection: bool = True) -> Processor: raise NotImplementedError("2+ Qubit gates not implemented") c_idx = qc.find_bit(instruction[1][0])[0] * 2 c_data = qc.find_bit(instruction[1][1])[0] * 2 - self._create_2_qubit_gates_from_catalog( - # instruction[0].name, - optimized_gate_sequence[gate_index], - n_cnot, - c_idx, - c_data, - use_postselection) + self._create_2_qubit_gates_from_catalog(optimized_gate_sequence[gate_index], c_idx, c_data, + use_postselection) self.apply_input_state() return self._converted_processor diff --git a/tests/test_converter_qiskit.py b/tests/test_converter_qiskit.py index ff967913..5950ac78 100644 --- a/tests/test_converter_qiskit.py +++ b/tests/test_converter_qiskit.py @@ -38,6 +38,8 @@ from perceval import BasicState, StateVector, Circuit from perceval.converters import QiskitConverter +from perceval.converters.qiskit_converter import _get_gate_sequence +from perceval.utils.converters import _label_cnots_in_gate_sequence import perceval.components.unitary_components as comp @@ -235,6 +237,20 @@ def test_cnot_ralph_vs_knill(): qisk_circ = qiskit_circ_multiple_cnots() converter = QiskitConverter() pc = converter.convert(qisk_circ) - import perceval as pcvl - pcvl.pdisplay(pc) - # todo : add assertions + + gate_sequence_converted = [] + for _, c in pc.components: + gate_sequence_converted.append(c.name) + + # gate list from qiskit + gate_sequence = _get_gate_sequence(qisk_circ) + optimized_gate_sequence = _label_cnots_in_gate_sequence(gate_sequence) + num_ralph_expt = len([elem for elem in optimized_gate_sequence if elem == 'CX:RALPH']) + cnot_order = [elem for elem in optimized_gate_sequence if elem.startswith('CX:')] + print(cnot_order) + cnot_order_pc = [elem for elem in gate_sequence_converted if elem.endswith('CNOT')] + print(cnot_order_pc) + + num_ralph_pc = len([elem for elem in gate_sequence_converted if elem == 'PostProcessed CNOT']) + assert num_ralph_pc == num_ralph_expt + # todo : hard code results of qiskit simulation