From cff832441df4b8c79c15893ab184ff3ca81f0721 Mon Sep 17 00:00:00 2001 From: AthenaCaesura Date: Wed, 15 Mar 2023 14:31:54 -0400 Subject: [PATCH 1/3] feat(circuit.py): add controlled() --- src/orquestra/quantum/circuits/_circuit.py | 11 +++++++++ .../quantum/circuits/_circuit_test.py | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/orquestra/quantum/circuits/_circuit.py b/src/orquestra/quantum/circuits/_circuit.py index 1263953..f7ee495 100644 --- a/src/orquestra/quantum/circuits/_circuit.py +++ b/src/orquestra/quantum/circuits/_circuit.py @@ -165,6 +165,17 @@ def inverse(self) -> "Circuit": " since there are operators in it without the `dagger` method." ) from e + def controlled(self): + """Return a circuit where all the operations from self are turned into their + respective controlled operations whose control is the 0th qubit.""" + c_ops = [] + for op in self.operations: + controlled_op = op.gate.controlled(1) + new_qubit_indices = (0, *(i + 1 for i in op.qubit_indices)) + c_ops.append(controlled_op(*new_qubit_indices)) + + return Circuit(c_ops) + @singledispatch def _append_to_circuit(other, circuit: Circuit): diff --git a/tests/orquestra/quantum/circuits/_circuit_test.py b/tests/orquestra/quantum/circuits/_circuit_test.py index d81a3e0..b8f467e 100644 --- a/tests/orquestra/quantum/circuits/_circuit_test.py +++ b/tests/orquestra/quantum/circuits/_circuit_test.py @@ -270,3 +270,26 @@ def test_inverting_a_circuit_without_dagger_fails(self): circuit = Circuit(operations=[gate]) with pytest.raises(AttributeError): circuit.inverse() + + +@pytest.mark.parametrize( + "circuit, target_circuit", + [ + (Circuit([X(0)]), Circuit([X.controlled(1)(0, 1)])), + (Circuit([H(0)]), Circuit([H.controlled(1)(0, 1)])), + (Circuit([CNOT(0, 1)]), Circuit([CNOT.controlled(1)(0, 1, 2)])), + ( + Circuit([X(0), CNOT(0, 1)]), + Circuit([X.controlled(1)(0, 1), CNOT.controlled(1)(0, 1, 2)]), + ), + ( + Circuit([X(2), CNOT(0, 1)]), + Circuit([X.controlled(1)(0, 3), CNOT.controlled(1)(0, 1, 2)]), + ), + ], +) +def test_controlled_circuit_gives_correct_output(circuit, target_circuit): + test_circuit = circuit.controlled() + + assert test_circuit.n_qubits == target_circuit.n_qubits + assert test_circuit == target_circuit From 6e37ed4cbdde5fb13265ad648346290d876ba11b Mon Sep 17 00:00:00 2001 From: AthenaCaesura Date: Fri, 17 Mar 2023 17:54:01 -0400 Subject: [PATCH 2/3] feat(circuit.py): choose control qubit --- src/orquestra/quantum/circuits/_circuit.py | 15 ++++++++--- .../quantum/circuits/_circuit_test.py | 25 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/orquestra/quantum/circuits/_circuit.py b/src/orquestra/quantum/circuits/_circuit.py index f7ee495..1fcb8cf 100644 --- a/src/orquestra/quantum/circuits/_circuit.py +++ b/src/orquestra/quantum/circuits/_circuit.py @@ -165,14 +165,21 @@ def inverse(self) -> "Circuit": " since there are operators in it without the `dagger` method." ) from e - def controlled(self): + def controlled(self, control_index) -> "Circuit": """Return a circuit where all the operations from self are turned into their - respective controlled operations whose control is the 0th qubit.""" + respective controlled operations whose control is the qubit with index given + by control_index. + + Args: + control_index: the index for the added qubit used to control the circuit + given by self. + """ c_ops = [] for op in self.operations: controlled_op = op.gate.controlled(1) - new_qubit_indices = (0, *(i + 1 for i in op.qubit_indices)) - c_ops.append(controlled_op(*new_qubit_indices)) + new_indices = (i + 1 if i >= control_index else i for i in op.qubit_indices) + new_indices_with_control = (control_index, *new_indices) + c_ops.append(controlled_op(*new_indices_with_control)) return Circuit(c_ops) diff --git a/tests/orquestra/quantum/circuits/_circuit_test.py b/tests/orquestra/quantum/circuits/_circuit_test.py index b8f467e..5a0fc56 100644 --- a/tests/orquestra/quantum/circuits/_circuit_test.py +++ b/tests/orquestra/quantum/circuits/_circuit_test.py @@ -273,23 +273,36 @@ def test_inverting_a_circuit_without_dagger_fails(self): @pytest.mark.parametrize( - "circuit, target_circuit", + "circuit, control_index, target_circuit", [ - (Circuit([X(0)]), Circuit([X.controlled(1)(0, 1)])), - (Circuit([H(0)]), Circuit([H.controlled(1)(0, 1)])), - (Circuit([CNOT(0, 1)]), Circuit([CNOT.controlled(1)(0, 1, 2)])), + (Circuit([X(0)]), 0, Circuit([X.controlled(1)(0, 1)])), + (Circuit([H(0)]), 0, Circuit([H.controlled(1)(0, 1)])), + (Circuit([X(0)]), 1, Circuit([X.controlled(1)(1, 0)])), + (Circuit([H(0)]), 4, Circuit([H.controlled(1)(4, 0)])), + (Circuit([X(2)]), 1, Circuit([X.controlled(1)(1, 3)])), + (Circuit([CNOT(0, 1)]), 0, Circuit([CNOT.controlled(1)(0, 1, 2)])), + (Circuit([CNOT(0, 1)]), 1, Circuit([CNOT.controlled(1)(1, 0, 2)])), ( Circuit([X(0), CNOT(0, 1)]), + 0, Circuit([X.controlled(1)(0, 1), CNOT.controlled(1)(0, 1, 2)]), ), ( Circuit([X(2), CNOT(0, 1)]), + 0, Circuit([X.controlled(1)(0, 3), CNOT.controlled(1)(0, 1, 2)]), ), + ( + Circuit([X(2), CNOT(0, 1)]), + 2, + Circuit([X.controlled(1)(2, 3), CNOT.controlled(1)(2, 0, 1)]), + ), ], ) -def test_controlled_circuit_gives_correct_output(circuit, target_circuit): - test_circuit = circuit.controlled() +def test_controlled_circuit_gives_correct_output( + circuit, control_index, target_circuit +): + test_circuit = circuit.controlled(control_index) assert test_circuit.n_qubits == target_circuit.n_qubits assert test_circuit == target_circuit From d596a854c9a3c8f9f6dbd90c969a9579ae36f4db Mon Sep 17 00:00:00 2001 From: AthenaCaesura Date: Fri, 17 Mar 2023 19:54:05 -0400 Subject: [PATCH 3/3] fix(circuit.py): add typing for qubit_index --- src/orquestra/quantum/circuits/_circuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/orquestra/quantum/circuits/_circuit.py b/src/orquestra/quantum/circuits/_circuit.py index 1fcb8cf..2c40b1c 100644 --- a/src/orquestra/quantum/circuits/_circuit.py +++ b/src/orquestra/quantum/circuits/_circuit.py @@ -165,7 +165,7 @@ def inverse(self) -> "Circuit": " since there are operators in it without the `dagger` method." ) from e - def controlled(self, control_index) -> "Circuit": + def controlled(self, control_index: int) -> "Circuit": """Return a circuit where all the operations from self are turned into their respective controlled operations whose control is the qubit with index given by control_index.