Skip to content
Draft
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
267 changes: 27 additions & 240 deletions mpqp/core/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from mpqp.core.instruction import Instruction
from mpqp.core.instruction.barrier import Barrier
from mpqp.core.instruction.breakpoint import Breakpoint
from mpqp.core.instruction.gates import ControlledGate, CRk, Gate, Id
from mpqp.core.instruction.gates import ControlledGate, Gate
from mpqp.core.instruction.gates.custom_controlled_gate import CustomControlledGate
from mpqp.core.instruction.gates.custom_gate import CustomGate
from mpqp.core.instruction.gates.parametrized_gate import ParametrizedGate
Expand Down Expand Up @@ -1128,6 +1128,7 @@ def to_other_language(
language: Literal[Language.QASM2, Language.QASM3],
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> str: ...

Expand All @@ -1137,6 +1138,7 @@ def to_other_language(
language: Literal[Language.CIRQ],
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> cirq_Circuit: ...

Expand All @@ -1146,6 +1148,7 @@ def to_other_language(
language: Literal[Language.BRAKET],
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> braket_Circuit: ...
@overload
Expand All @@ -1154,6 +1157,7 @@ def to_other_language(
language: Literal[Language.MY_QLM],
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> myQLM_Circuit: ...

Expand All @@ -1163,6 +1167,7 @@ def to_other_language(
language: Literal[Language.QISKIT],
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> QuantumCircuit: ...

Expand All @@ -1172,6 +1177,7 @@ def to_other_language(
language: Language,
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> QuantumCircuit | myQLM_Circuit | braket_Circuit | cirq_Circuit | str: ...

Expand All @@ -1180,6 +1186,7 @@ def to_other_language(
language: Language = Language.QISKIT,
skip_pre_measure: bool = False,
skip_measurements: bool = False,
authorized_gates: Optional[set[type[Gate]]] = None,
printing: bool = False,
) -> QuantumCircuit | myQLM_Circuit | braket_Circuit | cirq_Circuit | str:
"""Transforms this circuit into the corresponding circuit in the language
Expand Down Expand Up @@ -1253,265 +1260,45 @@ def to_other_language(
circuits.

"""
if authorized_gates is None:
authorized_gates = set()
self._generated_g_phase = 0
if language == Language.QISKIT:
from qiskit.circuit import Operation, QuantumCircuit
from qiskit.circuit.quantumcircuit import CircuitInstruction
from qiskit.quantum_info import Operator

# to avoid defining twice the same parameter, we keep trace of the
# added parameters, and we use those instead of new ones when they
# are used more than once
qiskit_parameters = set()
if self.nb_cbits == 0:
new_circ = QuantumCircuit(self.nb_qubits)
else:
new_circ = QuantumCircuit(self.nb_qubits, self.nb_cbits)

if self.label is not None:
new_circ.name = self.label

for instruction in self.instructions:
if isinstance(instruction, (Measure, Breakpoint)):
continue
options = (
{"printing": printing}
if isinstance(instruction, CustomGate)
else {}
)
qiskit_inst = instruction.to_other_language(
language, qiskit_parameters, **options
)
if TYPE_CHECKING:
assert isinstance(
qiskit_inst, (CircuitInstruction, Operation, Operator)
)
cargs = []

if isinstance(instruction, CustomGate):
if TYPE_CHECKING:
assert isinstance(qiskit_inst, Operator)
if printing and len(instruction.free_symbols) > 0:
new_circ.append(
qiskit_inst, list(reversed(instruction.targets))
)
else:
new_circ.unitary(
qiskit_inst,
list(reversed(instruction.targets)),
instruction.label,
)
else:
if isinstance(instruction, ControlledGate):
qargs = list(reversed(instruction.controls)) + list(
reversed(instruction.targets)
)
elif isinstance(instruction, Gate):
qargs = list(reversed(instruction.targets))
elif isinstance(instruction, Barrier):
qargs = range(self.nb_qubits)
else:
raise ValueError(f"Instruction not handled: {instruction}")

if TYPE_CHECKING:
assert not isinstance(qiskit_inst, Operator)
new_circ.append(
qiskit_inst,
list(qargs),
cargs,
)
from mpqp.tools.circuit import mpqp_to_qiskit

for measurement in self.measurements:
if not skip_pre_measure:

for pre_measure in measurement.pre_measure:
cargs = []
qiskit_pre_measure = pre_measure.to_other_language(
language, qiskit_parameters
)
new_circ.append(
qiskit_pre_measure,
list(reversed(pre_measure.targets)),
cargs=cargs,
)
if not skip_measurements:
if isinstance(measurement, ExpectationMeasure):
continue
qiskit_inst = measurement.to_other_language(
language, qiskit_parameters
)
if isinstance(measurement, BasisMeasure):
if TYPE_CHECKING:
assert measurement.c_targets is not None
else:
raise ValueError(f"measurement not handled: {measurement}")

if TYPE_CHECKING:
assert not isinstance(qiskit_inst, Operator)
new_circ.append(
qiskit_inst,
[measurement.targets],
[measurement.c_targets],
)

new_circ.global_phase += self.input_g_phase + self._generated_g_phase
return new_circ
return mpqp_to_qiskit(
self,
skip_pre_measure,
skip_measurements,
printing,
authorized_gates=authorized_gates,
)

elif language == Language.MY_QLM:
qasm2_code = self.to_other_language(
Language.QASM2,
skip_pre_measure=skip_pre_measure,
skip_measurements=True,
authorized_gates=authorized_gates,
)
from mpqp.qasm.qasm_to_myqlm import qasm2_to_myqlm_Circuit

myqlm_circuit = qasm2_to_myqlm_Circuit(qasm2_code)
return myqlm_circuit

elif language == Language.BRAKET:
# filling the circuit with identity gates when some qubits don't have any instruction
used_qubits = set().union(
*(
inst.connections()
for inst in self.instructions
if isinstance(inst, Gate)
)
)
circuit = QCircuit(
[
Id(qubit)
for qubit in range(self.nb_qubits)
if qubit not in used_qubits
],
nb_qubits=self.nb_qubits,
) + deepcopy(self)

from mpqp.execution.providers.aws import apply_noise_to_braket_circuit

if len(self.noises) != 0:
if any(isinstance(instr, CRk) for instr in self.instructions):
raise NotImplementedError(
"Cannot simulate noisy circuit with CRk gate due to "
"an error on AWS Braket side."
)
from braket.circuits import Circuit as BracketCircuit

braket_circuit = BracketCircuit()
for instruction in circuit.instructions:
if isinstance(instruction, (Barrier, Breakpoint)):
continue
if isinstance(instruction, Measure):
if not skip_pre_measure:
for pre_measure in instruction.pre_measure:
bracket_pre_measure = pre_measure.to_other_language(
Language.BRAKET
)
braket_circuit.add(bracket_pre_measure, instruction.targets)
if not skip_measurements:
if isinstance(instruction, BasisMeasure):
braket_circuit.measure(instruction.targets)
continue
braket_instr = instruction.to_other_language(Language.BRAKET)
try:
target = instruction.targets
if isinstance(instruction, ControlledGate):
target = instruction.controls + target
braket_circuit.add_instruction(braket_instr, target=target)
except Exception as e:
raise ValueError(
f"{type(braket_instr)}{braket_instr} cannot be added to the braket circuit: {e}"
)
from mpqp.tools.circuit import mpqp_to_braket

if len(self.noises) == 0:
return braket_circuit

return apply_noise_to_braket_circuit(
braket_circuit,
self.noises,
self.nb_qubits,
return mpqp_to_braket(
self, skip_pre_measure, authorized_gates=authorized_gates
)
elif language == Language.CIRQ:
from cirq.circuits.circuit import Circuit as CirqCircuit
from cirq.ops.identity import I
from cirq.ops.named_qubit import NamedQubit

cirq_qubits = [NamedQubit(f"q_{i}") for i in range(self.nb_qubits)]
cirq_circuit = CirqCircuit()

for qubit in cirq_qubits:
cirq_circuit.append(I(qubit))

for instruction in self.instructions:
if not skip_pre_measure:
if isinstance(instruction, Measure):
for pre_measure in instruction.pre_measure:
if isinstance(
pre_measure, (CustomGate, CustomControlledGate)
):
qasm2_code, gphase = pre_measure.to_other_language(
Language.QASM2
) # pyright: ignore[reportGeneralTypeIssues]
if TYPE_CHECKING:
assert isinstance(qasm2_code, str)
from mpqp.qasm.qasm_to_cirq import qasm2_to_cirq_Circuit

qasm2_code = qasm_str = (
"OPENQASM 2.0;"
+ "\ninclude \"qelib1.inc\";"
+ f"\nqreg q[{self.nb_qubits}];\n"
+ qasm2_code
)
custom_cirq_circuit = qasm2_to_cirq_Circuit(qasm2_code)
cirq_circuit += custom_cirq_circuit
# TODO: handle gphase in the circuit
self._generated_g_phase += gphase
else:
cirq_pre_measure = pre_measure.to_other_language(
Language.CIRQ
)
targets = []
for target in pre_measure.targets:
targets.append(cirq_qubits[target])
cirq_circuit.append(cirq_pre_measure.on(*targets))
if isinstance(instruction, (ExpectationMeasure, Barrier, Breakpoint)):
continue
elif isinstance(instruction, ControlledGate) and not isinstance(
instruction, CustomControlledGate
):
targets = []
for target in instruction.targets:
targets.append(cirq_qubits[target])
controls = []
for control in instruction.controls:
controls.append(cirq_qubits[control])
cirq_instruction = instruction.to_other_language(Language.CIRQ)
cirq_circuit.append(cirq_instruction.on(*controls, *targets))
else:
if skip_measurements and isinstance(instruction, Measure):
continue
targets = []
if isinstance(instruction, CustomControlledGate):
instruction = instruction.to_custom_gate()
for target in instruction.targets:
targets.append(cirq_qubits[target])
cirq_instruction = instruction.to_other_language(Language.CIRQ)
if TYPE_CHECKING:
assert cirq_instruction
cirq_circuit.append(
cirq_instruction.on( # pyright: ignore[reportAttributeAccessIssue]
*targets
)
)

if self.noises:
from mpqp.execution.providers.google import apply_noise_to_cirq_circuit

return apply_noise_to_cirq_circuit(
cirq_circuit,
self.noises,
)
elif language == Language.CIRQ:
from mpqp.tools.circuit import mpqp_to_cirq

return cirq_circuit
return mpqp_to_cirq(
self, skip_pre_measure, skip_measurements, authorized_gates
)

elif language == Language.QASM2:
from mpqp.qasm.mpqp_to_qasm import mpqp_to_qasm2
Expand Down
12 changes: 7 additions & 5 deletions mpqp/core/instruction/gates/custom_controlled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ class CustomControlledGate(ControlledGate):
>>> circuit = QCircuit(3)
>>> circuit.add(CustomControlledGate([0,2], CustomGate(np.array([[1,0],[0,-1]]),[1])))
>>> print(circuit) # doctest: +NORMALIZE_WHITESPACE
q_0: ─────■─────
┌────┴────┐
q_1: ┤ Unitary ├
└────┬────┘
q_2: ─────■─────
┌──────────┐
q_0: ┤2 ├
│ │
q_1: ┤1 Unitary ├
│ │
q_2: ┤0 ├
└──────────┘

"""

Expand Down
Loading
Loading