diff --git a/quafu/circuits/quantum_circuit.py b/quafu/circuits/quantum_circuit.py index ae15bb8..c9d5eaa 100644 --- a/quafu/circuits/quantum_circuit.py +++ b/quafu/circuits/quantum_circuit.py @@ -19,10 +19,10 @@ import quafu.elements.element_gates as qeg from quafu.elements.classical_element import Cif -from quafu.elements.quantum_element.instruction import Instruction -from quafu.elements.quantum_element.quantum_element import Measure, Reset +from quafu.elements.instruction import Instruction +from quafu.elements import Measure, Reset from quafu.elements.pulses import QuantumPulse -from ..elements.quantum_element import ( +from ..elements import ( Barrier, Delay, MultiQubitGate, @@ -42,8 +42,8 @@ class QuantumCircuit(object): """ Representation of quantum circuit. """ - - def __init__(self, qnum: int, cnum: int=None, *args, **kwargs): + + def __init__(self, qnum: int, cnum: int = None, *args, **kwargs): """ Initialize a QuantumCircuit object @@ -86,30 +86,30 @@ def used_qubits(self) -> List: @property def measures(self): measures = {} - for meas in self._measures: + for meas in self._measures: measures.update(dict(zip(meas.qbits, meas.cbits))) return measures @measures.setter - def measures(self, measures:dict): + def measures(self, measures: dict): self._measures = [Measure(measures)] - + @property def gates(self): """Deprecated warning: due to historical reason, ``gates`` contains not only instances of QuantumGate, meanwhile not contains measurements. This attributes might be deprecated in the future. Better to use ``instructions`` which contains all the instructions.""" return self._gates - + @gates.setter - def gates(self, gates:list): + def gates(self, gates: list): self._gates = gates - #TODO(qtzhuang): add_gates is just a temporary call function to add gate from gate_list + # TODO(qtzhuang): add_gates is just a temporary call function to add gate from gate_list def add_gates(self, gates: list): for gate in gates: self.add_gate(gate) - + def add_gate(self, gate: QuantumGate): """ Add quantum gate to circuit, with some checking. @@ -119,7 +119,7 @@ def add_gate(self, gate: QuantumGate): raise CircuitError(f"Gate position out of range: {gate.pos}") self.gates.append(gate) - def add_ins(self, ins:Instruction): + def add_ins(self, ins: Instruction): """ Add instruction to circuit, with NO checking yet. """ @@ -128,7 +128,7 @@ def add_ins(self, ins:Instruction): # Figure out better handling in the future. self.add_gate(ins) self.instructions.append(ins) - + def update_params(self, paras_list: List[Any]): """Update parameters of parameterized gates Args: @@ -159,18 +159,18 @@ def layered_circuit(self) -> np.ndarray: used_qubits = [] for gate in gatelist: if ( - isinstance(gate, SingleQubitGate) - or isinstance(gate, Delay) - or isinstance(gate, QuantumPulse) + isinstance(gate, SingleQubitGate) + or isinstance(gate, Delay) + or isinstance(gate, QuantumPulse) ): gateQlist[gate.pos].append(gate) if gate.pos not in used_qubits: used_qubits.append(gate.pos) elif ( - isinstance(gate, Barrier) - or isinstance(gate, MultiQubitGate) - or isinstance(gate, XYResonance) + isinstance(gate, Barrier) + or isinstance(gate, MultiQubitGate) + or isinstance(gate, XYResonance) ): pos1 = min(gate.pos) pos2 = max(gate.pos) @@ -190,25 +190,27 @@ def layered_circuit(self) -> np.ndarray: if not layerj == maxlayer: for i in range(abs(layerj - maxlayer)): gateQlist[j].insert(pos, None) + # Add support of used_qubits for Reset and Cif def get_used_qubits(instructions): used_q = [] for ins in instructions: - if(isinstance(ins, Cif)): + if (isinstance(ins, Cif)): used_q_h = get_used_qubits(ins.instructions) for pos in used_q_h: if pos not in used_q: used_q.append(pos) - elif(isinstance(ins, Barrier)): + elif (isinstance(ins, Barrier)): continue - elif(isinstance(ins.pos, int)): + elif (isinstance(ins.pos, int)): if ins.pos not in used_q: used_q.append(ins.pos) - elif(isinstance(ins.pos, list)): + elif (isinstance(ins.pos, list)): for pos in ins.pos: if pos not in used_q: used_q.append(pos) return used_q + # Only consider of reset and cif for ins in self.instructions: if isinstance(ins, (Reset, Cif)): @@ -216,8 +218,7 @@ def get_used_qubits(instructions): for pos in used_q: if pos not in used_qubits: used_qubits.append(pos) - - + maxdepth = max([len(gateQlist[i]) for i in range(num)]) for gates in gateQlist: @@ -262,9 +263,9 @@ def draw_circuit(self, width: int = 4, return_str: bool = False): for i in range(num): gate = layergates[i] if ( - isinstance(gate, SingleQubitGate) - or isinstance(gate, Delay) - or (isinstance(gate, QuantumPulse)) + isinstance(gate, SingleQubitGate) + or isinstance(gate, Delay) + or (isinstance(gate, QuantumPulse)) ): printlist[i * 2, l] = gate.symbol maxlen = max(maxlen, len(gate.symbol) + width) @@ -272,7 +273,7 @@ def draw_circuit(self, width: int = 4, return_str: bool = False): elif isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance): q1 = reduce_map[min(gate.pos)] q2 = reduce_map[max(gate.pos)] - printlist[2 * q1 + 1 : 2 * q2, l] = "|" + printlist[2 * q1 + 1: 2 * q2, l] = "|" printlist[q1 * 2, l] = "#" printlist[q2 * 2, l] = "#" if isinstance(gate, ControlledGate): # Controlled-Multiqubit gate @@ -308,7 +309,7 @@ def draw_circuit(self, width: int = 4, return_str: bool = False): pos = [i for i in gate.pos if i in reduce_map.keys()] q1 = reduce_map[min(pos)] q2 = reduce_map[max(pos)] - printlist[2 * q1 : 2 * q2 + 1, l] = "||" + printlist[2 * q1: 2 * q2 + 1, l] = "||" maxlen = max(maxlen, len("||")) printlist[-1, l] = maxlen @@ -825,7 +826,7 @@ def unitary(self, matrix: np.ndarray, pos: List[int]): """ compiler = qeg.UnitaryDecomposer(array=matrix, qubits=pos) compiler.apply_to_qc(self) - + def delay(self, pos, duration, unit="ns") -> "QuantumCircuit": """ Let the qubit idle for a certain duration. @@ -838,7 +839,7 @@ def delay(self, pos, duration, unit="ns") -> "QuantumCircuit": self.add_ins(Delay(pos, duration, unit=unit)) return self - def reset(self, qlist:List[int]= None) -> "QuantumCircuit": + def reset(self, qlist: List[int] = None) -> "QuantumCircuit": """ Add reset for qubits in qlist. @@ -852,7 +853,7 @@ def reset(self, qlist:List[int]= None) -> "QuantumCircuit": self.add_ins(Reset(qlist)) self.executable_on_backend = False return self - + def measure(self, pos: List[int] = None, cbits: List[int] = None) -> None: """ Measurement setting for experiment device. @@ -891,7 +892,7 @@ def measure(self, pos: List[int] = None, cbits: List[int] = None) -> None: self.add_ins(measure) @contextmanager - def cif(self, cbits:List[int], condition:int): + def cif(self, cbits: List[int], condition: int): """ Create an `if` statement on this circuit. If cbits equals to condition, the subsequent operaterations will be performed. @@ -918,14 +919,14 @@ def cif(self, cbits:List[int], condition:int): # check cbits if not len(set(cbits)) == len(cbits): raise ValueError("Classical bits not uniquely assigned.") - if max(cbits) > self.cbits_num -1 or min(cbits) < 0: + if max(cbits) > self.cbits_num - 1 or min(cbits) < 0: raise ValueError("Classical bits index out of range.") # check condition if condition < 0: raise ValueError("Classical should be a positive integer.") self.executable_on_backend = False cif_ins = Cif(cbits=cbits, condition=condition) - self.add_ins(cif_ins) + self.add_ins(cif_ins) yield @@ -934,7 +935,7 @@ def cif(self, cbits:List[int], condition:int): if isinstance(self.instructions[i], Cif) and self.instructions[i].instructions is None: instructions.reverse() self.instructions[i].set_ins(instructions) - self.instructions = self.instructions[0:i+1] + self.instructions = self.instructions[0:i + 1] return else: instructions.append(self.instructions[i]) @@ -947,4 +948,3 @@ def add_pulse(self, pulse: QuantumPulse, pos: int = None) -> "QuantumCircuit": pulse.set_pos(pos) self.add_ins(pulse) return self - diff --git a/quafu/elements/__init__.py b/quafu/elements/__init__.py index 4b3edf5..79c584e 100644 --- a/quafu/elements/__init__.py +++ b/quafu/elements/__init__.py @@ -1,4 +1,4 @@ -from .instruction import Instruction, Barrier, Measure +from .instruction import Instruction, Barrier, Measure, Reset from .pulses import Delay, XYResonance, QuantumPulse from .quantum_gate import QuantumGate, ControlledGate, MultiQubitGate, SingleQubitGate from .classical_element import Cif diff --git a/quafu/elements/instruction.py b/quafu/elements/instruction.py index 29af539..79a8089 100644 --- a/quafu/elements/instruction.py +++ b/quafu/elements/instruction.py @@ -16,7 +16,7 @@ from typing import Union, List, Dict -__all__ = ['Instruction', 'Barrier', 'Measure', 'PosType', 'ParaType'] +__all__ = ['Instruction', 'Barrier', 'Measure', 'PosType', 'ParaType', 'Reset'] PosType = Union[int, List[int]] ParaType = Union[float, int, List] @@ -118,6 +118,37 @@ def to_qasm(self): return "barrier " + ",".join(["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)]) +class Reset(Instruction): + name = 'reset' + + def __init__(self, pos): + super().__init__(pos) + + @property + def pos(self): + return self.__pos + + @pos.setter + def pos(self, pos): + self.__pos = pos + + @property + def named_pos(self): + return self.named_pos + + @property + def named_paras(self): + return self.named_paras + + def __repr__(self): + return f"{self.__class__.__name__}" + + def to_qasm(self): + return "reset " + ",".join( + ["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)] + ) + + class Measure(Instruction): """ Measure instruction. diff --git a/src/quafu/test.zip b/src/quafu/test.zip deleted file mode 100644 index 3114b2d..0000000 Binary files a/src/quafu/test.zip and /dev/null differ diff --git a/tests/quafu/simulator/classic_test.py b/tests/quafu/simulator/classic_test.py index 0ed5c0c..2a58fde 100644 --- a/tests/quafu/simulator/classic_test.py +++ b/tests/quafu/simulator/classic_test.py @@ -17,6 +17,7 @@ from base import BaseTest import unittest + class ClassicalCircuits: """Container for reference circuits used by the tests.""" @@ -24,69 +25,70 @@ class ClassicalCircuits: def cif_true(): qc = QuantumCircuit(2, 2) qc.x(0) - qc.measure([0],[0]) + qc.measure([0], [0]) with qc.cif([0], 1): qc.x(1) - qc.measure([1],[1]) + qc.measure([1], [1]) return qc @staticmethod def cif_false(): qc = QuantumCircuit(2) - qc.measure([0],[0]) + qc.measure([0], [0]) with qc.cif([0], 1): qc.x(1) - qc.measure([1],[1]) + qc.measure([1], [1]) return qc @staticmethod def cif_list_true(): qc = QuantumCircuit(3, 3) qc.x(0) - qc.cx(0,1) - qc.measure([0,1],[0,1]) - with qc.cif([0,1], 3): + qc.cx(0, 1) + qc.measure([0, 1], [0, 1]) + with qc.cif([0, 1], 3): qc.x(2) - qc.measure([2],[2]) + qc.measure([2], [2]) return qc - + @staticmethod def cif_list_false(): qc = QuantumCircuit(3) qc.x(0) - qc.cx(0,1) - qc.measure([0,1],[0,1]) - with qc.cif([0,1], 0): + qc.cx(0, 1) + qc.measure([0, 1], [0, 1]) + with qc.cif([0, 1], 0): qc.x(2) - with qc.cif([0,1], 1): + with qc.cif([0, 1], 1): qc.x(2) - with qc.cif([0,1], 2): + with qc.cif([0, 1], 2): qc.x(2) # skip '11'->3 - with qc.cif([0,1], 4): + with qc.cif([0, 1], 4): qc.x(2) - qc.measure([2],[2]) + qc.measure([2], [2]) return qc - + @staticmethod def single_reset(): qc = QuantumCircuit(2) qc.x(0) - qc.measure([0],[0]) + qc.measure([0], [0]) qc.reset([0]) - qc.measure([0],[1]) + qc.measure([0], [1]) return qc - + @staticmethod def multi_reset(): qc = QuantumCircuit(4) qc.x(0) - qc.cx(0,1) - qc.measure([0,1],[0,1]) - qc.reset([0,1]) - qc.measure([0,1],[2,3]) + qc.cx(0, 1) + qc.measure([0, 1], [0, 1]) + qc.reset([0, 1]) + qc.measure([0, 1], [2, 3]) return qc - + + class TestSimulatorClassic(BaseTest): """Test C++ simulator""" circuit = None @@ -95,7 +97,7 @@ class TestSimulatorClassic(BaseTest): assertDictEqual = unittest.TestCase.assertDictEqual assertListEqual = unittest.TestCase.assertListEqual assertTrue = unittest.TestCase.assertTrue - + def test_cif_true(self): self.circuit = ClassicalCircuits.cif_true() result = simulate(qc=self.circuit, shots=10) @@ -105,8 +107,8 @@ def test_cif_true(self): self.assertAlmostEqual(probs[1], 0) self.assertAlmostEqual(probs[2], 0) self.assertAlmostEqual(probs[3], 1) - self.assertDictAlmostEqual(count, {'11':10}) - + self.assertDictAlmostEqual(count, {'11': 10}) + def test_cif_false(self): self.circuit = ClassicalCircuits.cif_false() result = simulate(qc=self.circuit, shots=10) @@ -116,8 +118,8 @@ def test_cif_false(self): self.assertAlmostEqual(probs[1], 0) self.assertAlmostEqual(probs[2], 0) self.assertAlmostEqual(probs[3], 0) - self.assertDictAlmostEqual(count, {'00':10}) - + self.assertDictAlmostEqual(count, {'00': 10}) + def test_cif_list_true(self): self.circuit = ClassicalCircuits.cif_list_true() result = simulate(qc=self.circuit, shots=10) @@ -131,8 +133,8 @@ def test_cif_list_true(self): self.assertAlmostEqual(probs[5], 0) self.assertAlmostEqual(probs[6], 0) self.assertAlmostEqual(probs[7], 1) - self.assertDictAlmostEqual(count, {'111':10}) - + self.assertDictAlmostEqual(count, {'111': 10}) + def test_cif_list_false(self): self.circuit = ClassicalCircuits.cif_list_false() result = simulate(qc=self.circuit, shots=10) @@ -146,8 +148,8 @@ def test_cif_list_false(self): self.assertAlmostEqual(probs[5], 0) self.assertAlmostEqual(probs[6], 1) self.assertAlmostEqual(probs[7], 0) - self.assertDictAlmostEqual(count, {'110':10}) - + self.assertDictAlmostEqual(count, {'110': 10}) + def test_single_reset(self): self.circuit = ClassicalCircuits.single_reset() result = simulate(qc=self.circuit, shots=10) @@ -155,8 +157,8 @@ def test_single_reset(self): count = result.count self.assertAlmostEqual(probs[0], 1) self.assertAlmostEqual(probs[1], 0) - self.assertDictAlmostEqual(count, {'10':10}) - + self.assertDictAlmostEqual(count, {'10': 10}) + def test_multi_reset(self): self.circuit = ClassicalCircuits.multi_reset() result = simulate(qc=self.circuit, shots=10) @@ -166,4 +168,4 @@ def test_multi_reset(self): self.assertAlmostEqual(probs[1], 0) self.assertAlmostEqual(probs[2], 0) self.assertAlmostEqual(probs[3], 0) - self.assertDictAlmostEqual(count, {'1100':10}) \ No newline at end of file + self.assertDictAlmostEqual(count, {'1100': 10})