From b63f8cc87136927d71d33e32258ad0639de859fb Mon Sep 17 00:00:00 2001 From: Daniel Strano Date: Mon, 6 Sep 2021 16:38:50 -0400 Subject: [PATCH 1/5] Add PyQrack backend option --- .../quick/engine/test_pyqrackEngine.py | 320 +++++++++++++++++ simulaqron/virtNode/pyqrackSimulator.py | 333 ++++++++++++++++++ simulaqron/virtNode/virtual.py | 4 + 3 files changed, 657 insertions(+) create mode 100644 simulaqron/tests/unittests/quick/engine/test_pyqrackEngine.py create mode 100644 simulaqron/virtNode/pyqrackSimulator.py diff --git a/simulaqron/tests/unittests/quick/engine/test_pyqrackEngine.py b/simulaqron/tests/unittests/quick/engine/test_pyqrackEngine.py new file mode 100644 index 00000000..40eebd52 --- /dev/null +++ b/simulaqron/tests/unittests/quick/engine/test_pyqrackEngine.py @@ -0,0 +1,320 @@ +import unittest +import numpy as np + +from simulaqron.toolbox import has_module + +if has_module.main("pyqrack"): + + from simulaqron.virtNode.pyqrackSimulator import pyqrackEngine + from simulaqron.virtNode.basics import noQubitError, quantumError + + _has_module = True + +else: + + _has_module = False + + +def if_has_module(test): + def new_test(self): + if _has_module: + test(self) + + return new_test + + +class TestPyQrackEngine_init(unittest.TestCase): + @if_has_module + def test_init(self): + eng = pyqrackEngine("Alice", 0) + self.assertEqual(eng.maxQubits, 10) + self.assertEqual(eng.activeQubits, 0) + self.assertEqual(len(eng.qubitReg), 0) + + eng = pyqrackEngine("Alice", 0, 5) + self.assertEqual(eng.maxQubits, 5) + self.assertEqual(eng.activeQubits, 0) + self.assertEqual(len(eng.qubitReg), 0) + + +class TestPyQrackEngine(unittest.TestCase): + @if_has_module + def setUp(self): + self.eng = pyqrackEngine("Alice", 0) + + @staticmethod + def abs_inner_product(state, ref): + comb_state = np.array(state[0]) + 1j * np.array(state[1]) + inner = np.dot(comb_state, np.array(ref).conj()) + return np.abs(inner) + + @if_has_module + def test_add_fresh_qubit(self): + num = self.eng.add_fresh_qubit() + self.assertEqual(num, 0) + self.assertEqual(self.eng.activeQubits, 1) + self.assertEqual(len(self.eng.qubitReg), 1) + self.assertTrue(isinstance(self.eng.qubitReg[num], int)) + + @if_has_module + def test_add_to_many_fresh_qubits(self): + for _ in range(10): + self.eng.add_fresh_qubit() + with self.assertRaises(noQubitError): + self.eng.add_fresh_qubit() + + @if_has_module + def test_add_qubit(self): + new_state = [1, 0] + num = self.eng.add_qubit(new_state) + self.assertEqual(num, 0) + self.assertEqual(self.eng.activeQubits, 1) + self.assertEqual(len(self.eng.qubitReg), 1) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [0] * 10) + + @if_has_module + def test_add_qubit_H(self): + new_state = [1 / np.sqrt(2), 1 / np.sqrt(2)] + num = self.eng.add_qubit(new_state) + self.assertEqual(num, 0) + self.assertEqual(self.eng.activeQubits, 1) + self.assertEqual(len(self.eng.qubitReg), 1) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [0] * 10) + + @if_has_module + def test_add_unphysical_qubit(self): + new_state = [1, 1] + with self.assertRaises(quantumError): + self.eng.add_qubit(new_state) + + @if_has_module + def test_remove_qubit(self): + num = self.eng.add_fresh_qubit() + self.eng.remove_qubit(num) + self.assertEqual(self.eng.activeQubits, 0) + self.assertEqual(len(self.eng.qubitReg), 0) + with self.assertRaises(quantumError): + self.eng.remove_qubit(num) + + @if_has_module + def test_get_register_RI(self): + self.eng.add_fresh_qubit() + self.eng.add_fresh_qubit() + with self.assertRaises(NotImplementedError): + self.eng.get_register_RI() + + @if_has_module + def test_H(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_H(num) + self.eng.engine.z(num) + self.eng.engine.h(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_K(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_K(num) + self.eng.apply_Z(num) + self.eng.apply_H(num) + self.eng.engine.adjs(num) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [0] * 10) + + @if_has_module + def test_X(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_X(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_Y(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_Y(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + self.eng.apply_H(num) + self.eng.apply_Y(num) + self.eng.engine.h(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [0] * 10) + + @if_has_module + def test_Z(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_H(num) + self.eng.apply_Z(num) + self.eng.engine.h(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_Rx(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_rotation(num, (1, 0, 0), np.pi / 2) + self.eng.engine.adjs(0) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_Ry(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_rotation(num, (0, 1, 0), np.pi / 2) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [0] * 10) + + @if_has_module + def test_Rz(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_H(num) + self.eng.apply_rotation(num, (0, 0, 1), np.pi / 2) + self.eng.engine.s(0) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_faulty_rot(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_H(num) + with self.assertRaises(NotImplementedError): + self.eng.apply_rotation(num, (1, 0, 1), np.pi / 2) + + @if_has_module + def test_cnot(self): + num1 = self.eng.add_fresh_qubit() + num2 = self.eng.add_fresh_qubit() + self.eng.apply_X(num1) + self.eng.apply_CNOT(num1, num2) + results = self.eng.engine.measure_shots([num1, num2], 10) + self.assertEqual(results, [3] * 10) + + @if_has_module + def test_cz(self): + num1 = self.eng.add_fresh_qubit() + num2 = self.eng.add_fresh_qubit() + self.eng.apply_X(num1) + self.eng.apply_H(num2) + self.eng.apply_CPHASE(num1, num2) + self.eng.apply_H(num2) + results = self.eng.engine.measure_shots([num1, num2], 10) + self.assertEqual(results, [3] * 10) + + @if_has_module + def test_measure0(self): + num = self.eng.add_fresh_qubit() + m = self.eng.measure_qubit(num) + self.assertEqual(m, 0) + self.assertEqual(self.eng.activeQubits, 0) + + @if_has_module + def test_measure1(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_X(num) + m = self.eng.measure_qubit(num) + self.assertEqual(m, 1) + self.assertEqual(self.eng.activeQubits, 0) + + @if_has_module + def test_measure_inplace(self): + num = self.eng.add_fresh_qubit() + m = self.eng.measure_qubit_inplace(num) + self.assertEqual(m, 0) + self.assertEqual(self.eng.activeQubits, 1) + + @if_has_module + def test_absorb_both_empty(self): + eng2 = pyqrackEngine("Alice", 0) + self.eng.absorb(eng2) + self.assertEqual(self.eng.activeQubits, 0) + self.assertEqual(len(self.eng.qubitReg), 0) + + @if_has_module + def test_absorb_other_empty(self): + num = self.eng.add_fresh_qubit() + self.eng.apply_H(num) + eng2 = pyqrackEngine("Alice", 0) + self.eng.absorb(eng2) + self.assertEqual(self.eng.activeQubits, 1) + self.assertEqual(len(self.eng.qubitReg), 1) + self.eng.apply_Z(num) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_absorb_this_empty_H(self): + eng2 = pyqrackEngine("Alice", 0) + num = eng2.add_fresh_qubit() + eng2.apply_H(num) + self.eng.absorb(eng2) + self.assertEqual(self.eng.activeQubits, 1) + self.assertEqual(len(self.eng.qubitReg), 1) + self.eng.apply_Z(num) + self.eng.apply_H(num) + results = self.eng.engine.measure_shots([0], 10) + self.assertEqual(results, [1] * 10) + + @if_has_module + def test_absorb_this_empty_CNOT(self): + eng2 = pyqrackEngine("Alice", 0) + num1 = eng2.add_fresh_qubit() + num2 = eng2.add_fresh_qubit() + eng2.apply_X(num1) + eng2.apply_CNOT(num1, num2) + self.eng.absorb(eng2) + self.assertEqual(self.eng.activeQubits, 2) + self.assertEqual(len(self.eng.qubitReg), 2) + results = self.eng.engine.measure_shots([num1, num2], 10) + self.assertEqual(results, [3] * 10) + + @if_has_module + def test_absorb_2GHZ(self): + n = 5 + eng2 = pyqrackEngine("Alice", 0) + for eng in [self.eng, eng2]: + qubits = [eng.add_fresh_qubit() for _ in range(n)] + eng.apply_H(qubits[0]) + for i in range(1, n): + eng.apply_CNOT(qubits[0], qubits[i]) + self.eng.absorb(eng2) + self.assertEqual(self.eng.activeQubits, 2 * n) + self.assertEqual(len(self.eng.qubitReg), 2 * n) + + @if_has_module + def test_absorb_to_big_this_empty(self): + eng2 = pyqrackEngine("Alice", 0, 11) + for _ in range(11): + eng2.add_fresh_qubit() + with self.assertRaises(quantumError): + self.eng.absorb(eng2) + + @if_has_module + def test_absorb_to_big(self): + self.eng.add_fresh_qubit() + eng2 = pyqrackEngine("Alice", 0) + for _ in range(10): + eng2.add_fresh_qubit() + with self.assertRaises(quantumError): + self.eng.absorb(eng2) + + @if_has_module + def test_absorb_parts(self): + self.eng.add_fresh_qubit() + eng2 = pyqrackEngine("Alice", 0) + eng2.add_fresh_qubit() + with self.assertRaises(NotImplementedError): + self.eng.absorb_parts([], [], eng2.activeQubits) + + +if __name__ == "__main__": + if _has_module: + unittest.main() diff --git a/simulaqron/virtNode/pyqrackSimulator.py b/simulaqron/virtNode/pyqrackSimulator.py new file mode 100644 index 00000000..daa58433 --- /dev/null +++ b/simulaqron/virtNode/pyqrackSimulator.py @@ -0,0 +1,333 @@ +# +# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Stephanie Wehner, QuTech. +# 4. Neither the name of the QuTech organization nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +try: + from pyqrack import QrackSimulator, Pauli +except ImportError: + raise RuntimeError("If you want to use the pyqrack backend you need to install the python package 'pyqrack'") +import numpy as np + +from simulaqron.virtNode.basics import quantumEngine, quantumError, noQubitError + + +class pyqrackEngine(quantumEngine): + """ + Basic quantum engine which uses PyQrack. + + Attributes: + maxQubits: maximum number of qubits this engine will support. + """ + + def __init__(self, node, num, maxQubits=10): + """ + Initialize the simple engine. If no number is given for maxQubits, the assumption will be 10. + """ + + super().__init__(node=node, num=num, maxQubits=maxQubits) + + self.engine = QrackSimulator() + + # We start with no active qubits + self.activeQubits = 0 + self.nextQid = 0 + self.qubitReg = [] + + def add_fresh_qubit(self): + """ + Add a new qubit initialized in the \|0\> state. + """ + # Check if we are still allowed to add qubits + if self.activeQubits >= self.maxQubits: + raise noQubitError("No more qubits available in register.") + + # Prepare a clean qubit state in |0> + qid = self.nextQid + self.nextQid += 1 + self.engine.allocate_qubit(qid) + self.activeQubits += 1 + self.qubitReg.append(qid) + + return qid + + def add_qubit(self, newQubit): + """ + Add new qubit in the state described by the vector newQubit ([a, b]) + """ + + norm = np.dot(np.array(newQubit), np.array(newQubit).conj()) + if not norm <= 1: + raise quantumError("State {} is not normalized.".format(newQubit)) + + # Create a fresh qubit + qid = self.add_fresh_qubit() + + # Find an appropriate state preparation gate + prob = np.dot(complex(newQubit[1]), np.conj(complex(newQubit[1]))) + sqrtProb = np.sqrt(prob) + sqrt1MinProb = np.sqrt(1 - prob) + + phase0 = 0 + if sqrt1MinProb > 0: + phase0 = complex(newQubit[0]) / sqrt1MinProb + + phase1 = 0 + if sqrtProb > 0: + phase1 = complex(newQubit[1]) / sqrt1MinProb + + cMtrx = [sqrt1MinProb * phase0, sqrtProb * phase0, sqrtProb * phase1, -sqrt1MinProb * phase1] + dMtrx = [ + cMtrx[0].real, cMtrx[0].imag, cMtrx[1].real, cMtrx[1].imag, + cMtrx[2].real, cMtrx[2].imag, cMtrx[3].real, cMtrx[3].imag + ] + + # Transform the new qubit into the correct state + self.engine.mtrx(dMtrx, qid) + + return qid + + def validate_qid(self, qid): + if qid not in self.qubitReg: + raise quantumError("No such qubit to remove") + + def remove_qubit(self, qubitNum): + """ + Removes the qubit with the desired number qubitNum + """ + self.validate_qid(qubitNum) + + self.engine.release(qubitNum) + self.qubitReg.remove(qubitNum) + self.activeQubits -= 1 + + def get_register_RI(self): + """ + Retrieves the entire register in real and imaginary parts and returns the result as a + list. Twisted only likes to send real valued lists, not complex ones. + """ + raise NotImplementedError("get_register_RI() not implemented for this backend!") + + def apply_H(self, qubitNum): + """ + Applies a Hadamard gate to the qubits with number qubitNum. + """ + self.validate_qid(qubitNum) + + self.engine.h(qubitNum) + + def apply_K(self, qubitNum): + """ + Applies a K gate to the qubits with number qubitNum. Maps computational basis to Y eigenbasis. + """ + self.validate_qid(qubitNum) + + self.engine.h(qubitNum) + self.engine.s(qubitNum) + self.engine.h(qubitNum) + self.engine.z(qubitNum) + + def apply_X(self, qubitNum): + """ + Applies a X gate to the qubits with number qubitNum. + """ + self.validate_qid(qubitNum) + + self.engine.x(qubitNum) + + def apply_Z(self, qubitNum): + """ + Applies a Z gate to the qubits with number qubitNum. + """ + self.validate_qid(qubitNum) + + self.engine.z(qubitNum) + + def apply_Y(self, qubitNum): + """ + Applies a Y gate to the qubits with number qubitNum. + """ + self.validate_qid(qubitNum) + + self.engine.y(qubitNum) + + def apply_T(self, qubitNum): + """ + Applies a T gate to the qubits with number qubitNum. + """ + self.validate_qid(qubitNum) + + self.engine.t(qubitNum) + + def apply_rotation(self, qubitNum, n, a): + """ + Applies a rotation around the axis n with the angle a to qubit with number qubitNum. If n is zero a ValueError + is raised. + + :param qubitNum: int + Qubit number + :param n: tuple of floats + A tuple of three numbers specifying the rotation axis, e.g n=(1,0,0) + :param a: float + The rotation angle in radians. + """ + self.validate_qid(qubitNum) + + n = tuple(n) + if n == (1, 0, 0): + self.engine.r(Pauli.PauliX, a, qubitNum) + elif n == (0, 1, 0): + self.engine.r(Pauli.PauliY, a, qubitNum) + elif n == (0, 0, 1): + self.engine.r(Pauli.PauliZ, a, qubitNum) + else: + raise NotImplementedError("Can only do rotations around X, Y, or Z axis right now") + + def validate_control_qids(self, qid1, qid2): + if qid1 not in self.qubitReg: + raise quantumError("No such qubit to act as a control qubit") + + if qid2 not in self.qubitReg: + raise quantumError("No such qubit to act as a target qubit") + + if qid1 == qid2: + raise quantumError("Control and target are equal") + + def apply_CNOT(self, qubitNum1, qubitNum2): + """ + Applies the CNOT to the qubit with the numbers qubitNum1 and qubitNum2. + """ + self.validate_control_qids(qubitNum1, qubitNum2) + + self.engine.mcx([qubitNum1], qubitNum2) + + def apply_CPHASE(self, qubitNum1, qubitNum2): + """ + Applies the CPHASE to the qubit with the numbers qubitNum1 and qubitNum2. + """ + self.validate_control_qids(qubitNum1, qubitNum2) + + self.engine.mcz([qubitNum1], qubitNum2) + + def apply_onequbit_gate(self, gate, qubitNum): + """ + Applies a unitary gate to the specified qubit. + + Arguments: + gate The project Q gate to be applied + qubitNum the number of the qubit this gate is applied to + """ + self.validate_qid(qubitNum) + + self.engine.mtrx(gate, qubitNum) + + def apply_twoqubit_gate(self, gate, qubit1, qubit2): + """ + Applies a unitary gate to the two specified qubits. + + Arguments: + gate The project Q gate to be applied + qubit1 the first qubit + qubit2 the second qubit + """ + raise NotImplementedError("apply_twoqubit_gate() not implemented for this backend!") + + def measure_qubit_inplace(self, qubitNum): + """ + Measures the desired qubit in the standard basis. This returns the classical outcome. The quantum register + is in the post-measurment state corresponding to the obtained outcome. + + Arguments: + qubitNum qubit to be measured + """ + + # Check we have such a qubit... + self.validate_qid(qubitNum) + + outcome = self.engine.m(qubitNum) + + # return measurement outcome + return outcome + + def measure_qubit(self, qubitNum): + """ + Measures the desired qubit in the standard basis. This returns the classical outcome and deletes the qubit. + + Arguments: + qubitNum qubit to be measured + """ + outcome = self.measure_qubit_inplace(qubitNum) + + self.remove_qubit(qubitNum) + + return outcome + + def replace_qubit(self, qubitNum, state): + """ + Replaces the qubit at position qubitNum with the one given by state. + """ + raise NotImplementedError("Currently you cannot replace a qubit using pyqrack as backend") + + def absorb(self, other): + """ + Absorb the qubits from the other engine into this one. This is done by tensoring the state at the end. + """ + + # Check whether there is space + newNum = self.activeQubits + other.activeQubits + if newNum > self.maxQubits: + raise quantumError("Cannot merge: qubits exceed the maximum available.\n") + + # Check whether there are in fact qubits to tensor up.... + if self.activeQubits == 0: + self.engine = other.engine + self.qubitReg = list(other.qubitReg) + self.nextQid = other.nextQid + elif other.activeQubits > 0: + # PyQrack can internally "compose" the two engines together. + nQubits = [] + for q in range(other.activeQubits): + nQubits.append(self.nextQid) + self.nextQid += 1 + + self.engine.compose(other.engine, nQubits) + + # Add the qubits to the list of qubits + self.qubitReg += nQubits + + self.activeQubits = newNum + + def absorb_parts(self, R, I, activeQ): + """ + Absorb the qubits, given in pieces + + Arguments: + R real part of the qubit state as a list + I imaginary part as a list + activeQ active number of qubits + """ + raise NotImplementedError("Currently you cannot absorb_parts() using pyqrack as backend") diff --git a/simulaqron/virtNode/virtual.py b/simulaqron/virtNode/virtual.py index 44821825..2e292a11 100644 --- a/simulaqron/virtNode/virtual.py +++ b/simulaqron/virtNode/virtual.py @@ -48,6 +48,8 @@ from simulaqron.virtNode.qutipSimulator import qutipEngine elif simulaqron_settings.backend == "projectq": from simulaqron.virtNode.projectQSimulator import projectQEngine +elif simulaqron_settings.backend == "pyqrack": + from simulaqron.virtNode.pyqrackSimulator import pyqrackEngine elif simulaqron_settings.backend == "stabilizer": from simulaqron.virtNode.stabilizerSimulator import stabilizerEngine else: @@ -446,6 +448,8 @@ def remote_new_register(self, maxQubits=10): newReg = qutipEngine(self.myID, regNum, maxQubits) elif simulaqron_settings.backend == "projectq": newReg = projectQEngine(self.myID, regNum, maxQubits) + elif simulaqron_settings.backend == "pyqrack": + newReg = pyqrackEngine(self.myID, regNum, maxQubits) elif simulaqron_settings.backend == "stabilizer": newReg = stabilizerEngine(self.myID, regNum, maxQubits) else: From 4ffba7c128e4b51d83f626a22ebe16abd6abf5f6 Mon Sep 17 00:00:00 2001 From: Daniel Strano Date: Thu, 9 Sep 2021 13:49:24 -0400 Subject: [PATCH 2/5] Prep for pyqrack==0.3.0 release --- simulaqron/virtNode/pyqrackSimulator.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/simulaqron/virtNode/pyqrackSimulator.py b/simulaqron/virtNode/pyqrackSimulator.py index daa58433..5698dda6 100644 --- a/simulaqron/virtNode/pyqrackSimulator.py +++ b/simulaqron/virtNode/pyqrackSimulator.py @@ -101,13 +101,9 @@ def add_qubit(self, newQubit): phase1 = complex(newQubit[1]) / sqrt1MinProb cMtrx = [sqrt1MinProb * phase0, sqrtProb * phase0, sqrtProb * phase1, -sqrt1MinProb * phase1] - dMtrx = [ - cMtrx[0].real, cMtrx[0].imag, cMtrx[1].real, cMtrx[1].imag, - cMtrx[2].real, cMtrx[2].imag, cMtrx[3].real, cMtrx[3].imag - ] # Transform the new qubit into the correct state - self.engine.mtrx(dMtrx, qid) + self.engine.mtrx(cMtrx, qid) return qid @@ -238,7 +234,7 @@ def apply_onequbit_gate(self, gate, qubitNum): Applies a unitary gate to the specified qubit. Arguments: - gate The project Q gate to be applied + gate The pyqrack gate to be applied qubitNum the number of the qubit this gate is applied to """ self.validate_qid(qubitNum) @@ -250,7 +246,7 @@ def apply_twoqubit_gate(self, gate, qubit1, qubit2): Applies a unitary gate to the two specified qubits. Arguments: - gate The project Q gate to be applied + gate The pyqrack gate to be applied qubit1 the first qubit qubit2 the second qubit """ From 574a9f05eb7f405fd18b099ec4d106c662aa2616 Mon Sep 17 00:00:00 2001 From: Daniel Strano Date: Wed, 22 Sep 2021 10:58:17 -0400 Subject: [PATCH 3/5] Fix pyqrack teleport example --- examples/nativeMode/teleport/aliceTest.py | 2 ++ examples/nativeMode/teleport/bobTest.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/nativeMode/teleport/aliceTest.py b/examples/nativeMode/teleport/aliceTest.py index 0660f63f..acf34c0a 100644 --- a/examples/nativeMode/teleport/aliceTest.py +++ b/examples/nativeMode/teleport/aliceTest.py @@ -78,6 +78,8 @@ def runClientNode(qReg, virtRoot, myName, classicalNet): elif simulaqron_settings.backend == "stabilizer": array, _ = yield virtRoot.callRemote("get_register_RI", q1) state = StabilizerState(array) + elif simulaqron_settings.backend == "pyqrack": + state = '(Not directly queryable with PyQrack)' else: ValueError("Unknown backend {}".format(simulaqron_settings.backend)) diff --git a/examples/nativeMode/teleport/bobTest.py b/examples/nativeMode/teleport/bobTest.py index fb4b8329..8d363d27 100644 --- a/examples/nativeMode/teleport/bobTest.py +++ b/examples/nativeMode/teleport/bobTest.py @@ -121,6 +121,8 @@ def remote_recover_teleport(self, a, b, virtualNum): elif simulaqron_settings.backend == "stabilizer": array, _, = yield self.virtRoot.callRemote("get_register_RI", eprB) state = StabilizerState(array) + elif simulaqron_settings.backend == "pyqrack": + state = '(Not directly queryable with PyQrack)' else: ValueError("Unknown backend {}".format(simulaqron_settings.backend)) From ed356cac647ff45ad48be18e74e51e75f203e324 Mon Sep 17 00:00:00 2001 From: Daniel Strano Date: Mon, 27 Sep 2021 13:04:30 -0400 Subject: [PATCH 4/5] Add PyQrack CLI backend choice --- simulaqron/SimulaQron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulaqron/SimulaQron.py b/simulaqron/SimulaQron.py index 6fc3bf38..8a0c0544 100755 --- a/simulaqron/SimulaQron.py +++ b/simulaqron/SimulaQron.py @@ -231,7 +231,7 @@ def default(): @set.command() -@click.argument('value', type=click.Choice(["stabilizer", "projectq", "qutip"])) +@click.argument('value', type=click.Choice(["stabilizer", "projectq", "qutip", "pyqrack"])) def backend(value): """The backend to use (stabilizer, projectq, qutip).""" simulaqron_settings.backend = value From eaa5548df2f992e187ee70ccd81f192a1ce93e14 Mon Sep 17 00:00:00 2001 From: Daniel Strano Date: Wed, 15 Dec 2021 11:10:33 -0500 Subject: [PATCH 5/5] Fix SimBackend iteration --- simulaqron/simulaqron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulaqron/simulaqron.py b/simulaqron/simulaqron.py index e4facb7e..ae8171a9 100644 --- a/simulaqron/simulaqron.py +++ b/simulaqron/simulaqron.py @@ -231,7 +231,7 @@ def default(): @set.command() -@click.argument('value', type=click.Choice([b.value for b in SimBackend.value])) +@click.argument('value', type=click.Choice([b.value for b in SimBackend])) def sim_backend(value): """The backend to use (stabilizer, projectq, pyqrack, qutip).""" simulaqron_settings.sim_backend = value