From ffef7c6ea55a246b0dcd71ea9c4c0ff4ec31b0e5 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sun, 5 Nov 2023 17:35:46 +0800 Subject: [PATCH 01/11] adjust format --- quafu/circuits/quantum_circuit.py | 70 +++++++++---------- .../quantum_element/classical_element.py | 11 +-- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/quafu/circuits/quantum_circuit.py b/quafu/circuits/quantum_circuit.py index 68661b6..8a4388e 100644 --- a/quafu/circuits/quantum_circuit.py +++ b/quafu/circuits/quantum_circuit.py @@ -43,8 +43,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 @@ -87,32 +87,32 @@ 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 - + def add_gate(self, gate: QuantumGate): pos = np.array(gate.pos) if np.any(pos >= self.num): 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. """ @@ -121,7 +121,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: @@ -152,18 +152,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) @@ -183,25 +183,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)): @@ -209,8 +211,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: @@ -255,9 +256,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) @@ -265,7 +266,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 @@ -301,7 +302,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 @@ -806,7 +807,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. @@ -819,7 +820,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. @@ -831,7 +832,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. @@ -870,7 +871,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. @@ -895,14 +896,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 @@ -911,7 +912,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]) @@ -924,4 +925,3 @@ def add_pulse(self, pulse: QuantumPulse, pos: int = None) -> "QuantumCircuit": pulse.set_pos(pos) self.add_ins(pulse) return self - \ No newline at end of file diff --git a/quafu/elements/quantum_element/classical_element.py b/quafu/elements/quantum_element/classical_element.py index 5b70c11..d4b8f2d 100644 --- a/quafu/elements/quantum_element/classical_element.py +++ b/quafu/elements/quantum_element/classical_element.py @@ -17,15 +17,18 @@ from typing import List from .instruction import Instruction + class Cif(Instruction): name = 'cif' - def __init__(self, cbits:List[int], condition:int, instructions=None): + + def __init__(self, cbits: List[int], condition: int, instructions=None): # cbit can be a list of cbit or just a cbit self.cbits = cbits self.condition = condition self.instructions = instructions - + def set_ins(self, instructions: List[Instruction]): - self.instructions = instructions + self.instructions = instructions + -Instruction.register_ins(Cif) \ No newline at end of file +Instruction.register_ins(Cif) From e0d3040790b86367d3426dfdadcc2f9ccef6d33f Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Mon, 22 Jan 2024 14:42:43 +0800 Subject: [PATCH 02/11] chore: add interface ``circuit`` --- quafu/elements/oracle.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/quafu/elements/oracle.py b/quafu/elements/oracle.py index 070bf0e..513015b 100644 --- a/quafu/elements/oracle.py +++ b/quafu/elements/oracle.py @@ -91,6 +91,14 @@ def to_qasm(self): # TODO: this is similar to QuantumCircuit.to_qasm raise NotImplemented + @property + def circuit(self): + from quafu import QuantumCircuit + qc = QuantumCircuit(self.qubit_num) + for gate in self.insides: + qc.add_gate(gate) + return qc + def __instantiate_gates__(self) -> None: """ Instantiate the gate structure through coping ins and bit mapping. From c5bf4ce7f4f14542a68478560b3d665beb4fe38e Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 15:55:53 +0800 Subject: [PATCH 03/11] chore: unify interface of pos and paras of Instruction --- quafu/elements/instruction.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/quafu/elements/instruction.py b/quafu/elements/instruction.py index 7994fcb..8e2ff0f 100644 --- a/quafu/elements/instruction.py +++ b/quafu/elements/instruction.py @@ -13,13 +13,13 @@ # limitations under the License. from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Any from .parameters import ParameterType __all__ = ["Instruction", "Barrier", "Measure", "PosType", "Reset"] -PosType = Union[int, List[int]] +PosType = Union[int] # TODO: include qubits class Instruction(ABC): @@ -27,7 +27,7 @@ class Instruction(ABC): Attributes: pos: Qubit position(s) of the instruction on the circuit. - paras: Parameters of the instruction. + paras: Parameter list of the instruction. """ @@ -35,14 +35,32 @@ class Instruction(ABC): def __init__( self, - pos: PosType, + pos: Union[PosType, List[PosType]], paras: Optional[Union[ParameterType, List[ParameterType]]] = None, *args, **kwargs, ): + self.__init_pos__(pos) + self.__init_paras__(paras) + self._symbol = None + + def __init_pos__(self, pos: Any): + """If pos is not a list of PoseType objects, make it so.""" + if isinstance(pos, PosType): + pos = [pos] + elif not isinstance(pos, List): + raise ValueError('pos must be in PoseType or a list of PoseType objects') self.pos = pos + + def __init_paras__(self, paras: Any): + """If paras is not a list of ParameterType objects, make it so.""" + if paras is None: + paras = [] + elif not isinstance(paras, List): + if not isinstance(paras, ParameterType): + raise ValueError('paras must be in ParameterType or a list of ParameterType objects') + paras = [paras] self.paras = paras - self._symbol = None @property @abstractmethod From 460794d6e098ad85b425f4a8105fb5da04b92901 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 15:57:47 +0800 Subject: [PATCH 04/11] chore: register reset --- quafu/elements/instruction.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/quafu/elements/instruction.py b/quafu/elements/instruction.py index 8e2ff0f..fd5e933 100644 --- a/quafu/elements/instruction.py +++ b/quafu/elements/instruction.py @@ -113,6 +113,7 @@ def to_qasm(self): pass +@Instruction.register('barrier') class Barrier(Instruction): """ Barrier instruction. @@ -155,6 +156,7 @@ def to_qasm(self): ) +@Instruction.register('reset') class Reset(Instruction): name = "reset" @@ -186,6 +188,7 @@ def to_qasm(self): ) +@Instruction.register('measure') class Measure(Instruction): """ Measure instruction. @@ -213,7 +216,3 @@ def to_qasm(self): ] qasm = "".join(lines) return qasm - - -Instruction.register_ins(Barrier) -Instruction.register_ins(Measure) From 27c97f51d09f05c7792f3b1915387f66004937d4 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 16:13:24 +0800 Subject: [PATCH 05/11] chore: fulfill docstring --- quafu/elements/instruction.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/quafu/elements/instruction.py b/quafu/elements/instruction.py index fd5e933..d86f682 100644 --- a/quafu/elements/instruction.py +++ b/quafu/elements/instruction.py @@ -26,9 +26,17 @@ class Instruction(ABC): """Base class for ALL the possible instructions on Quafu superconducting quantum circuits. Attributes: - pos: Qubit position(s) of the instruction on the circuit. + pos: Qubit position(s) list of the instruction on the circuit. paras: Parameter list of the instruction. + Properties: + symbol: Text symbolic representation of this gate. + + Functions: + register_gate: Register a new gate class. + to_qasm: Convert this gate to QASM format. + update_paras: Update the parameters of this gate. + """ ins_classes = {} From 3345faa076b5cb7b8d93814b6fd7f1567c49f769 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 16:13:52 +0800 Subject: [PATCH 06/11] chore: fulfill docstring and typing --- quafu/elements/quantum_gate.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/quafu/elements/quantum_gate.py b/quafu/elements/quantum_gate.py index a2482ca..40ecaa6 100644 --- a/quafu/elements/quantum_gate.py +++ b/quafu/elements/quantum_gate.py @@ -61,12 +61,7 @@ class QuantumGate(Instruction, ABC): """Base class for standard and combined quantum gates, namely unitary operation upon quantum states. - Attributes: - pos: Position of this gate in the circuit. - paras: Parameters of this gate. - Properties: - symbol: Text symbolic representation of this gate. matrix: Matrix representation of this gate. Functions: @@ -74,13 +69,14 @@ class QuantumGate(Instruction, ABC): to_qasm: Convert this gate to QASM format. update_paras: Update the parameters of this gate. + see docstring of Instruction for other members. """ gate_classes = {} def __init__( self, - pos: PosType, + pos: Union[PosType, List[PosType]], paras: Optional[Union[ParameterType, List[ParameterType]]] = None, matrix: Optional[Union[ndarray, Callable]] = None, ): From 6fc30b501be246e2fde289a7d42bc53679d0860e Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 16:14:10 +0800 Subject: [PATCH 07/11] chore: implementing to_qasm() --- quafu/elements/oracle.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/quafu/elements/oracle.py b/quafu/elements/oracle.py index 513015b..82aad6c 100644 --- a/quafu/elements/oracle.py +++ b/quafu/elements/oracle.py @@ -88,8 +88,11 @@ def named_paras(self) -> Dict: return self._named_pos def to_qasm(self): - # TODO: this is similar to QuantumCircuit.to_qasm - raise NotImplemented + if len(self.gate_structure) > 0: + return super().to_qasm() + else: + # TODO: decompose matrix in a Just-In-Time style + raise NotImplemented @property def circuit(self): From cc0a245b358b4531a7bfabbcaa9e63a1a4eb4553 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 16:50:49 +0800 Subject: [PATCH 08/11] chore: implement matrix --- quafu/elements/quantum_gate.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/quafu/elements/quantum_gate.py b/quafu/elements/quantum_gate.py index 40ecaa6..00102b4 100644 --- a/quafu/elements/quantum_gate.py +++ b/quafu/elements/quantum_gate.py @@ -14,7 +14,7 @@ import copy -from abc import ABC, abstractmethod +from abc import ABC from typing import Callable, Dict, Iterable, List, Optional, Union import numpy as np @@ -156,14 +156,17 @@ def symbol(self, symbol: str): self._symbol = symbol @property - @abstractmethod - def matrix(self): - if self._matrix is not None: + def matrix(self) -> ndarray: + """Return numpy.ndarray matrix representation, real-time updated with parameters, if any.""" + if isinstance(self._matrix, ndarray): return self._matrix + elif isinstance(self._matrix, Callable): + paras = [float(p) for p in self.paras] # TODO: check that if float() is well supported + return self._matrix(paras) else: raise NotImplementedError( "Matrix is not implemented for %s" % self.__class__.__name__ - + ", this should never happen." + + ". This should never happen, please report it on our github page." ) def to_qasm(self) -> str: @@ -404,11 +407,6 @@ def symbol(self): def symbol(self, symbol): self._symbol = symbol - @property - def matrix(self): - # TODO: update matrix when paras of controlled-gate changed - return self._matrix - @property def ct_nums(self): targ_num = len(self.targs) From 9d9070b9af3c9db8cab0284883941e33c24db0b1 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 27 Jan 2024 16:59:47 +0800 Subject: [PATCH 09/11] chore: unpack paras --- quafu/elements/quantum_gate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quafu/elements/quantum_gate.py b/quafu/elements/quantum_gate.py index 00102b4..9c10134 100644 --- a/quafu/elements/quantum_gate.py +++ b/quafu/elements/quantum_gate.py @@ -417,7 +417,7 @@ def ct_nums(self): @property def targ_matrix(self): if isinstance(self._targ_matrix, Callable): - return self._targ_matrix(self.paras) + return self._targ_matrix(*self.paras) else: return self._targ_matrix From dfe0cc53de8595ec0941c1b5ed8d8ffe5e2c9edf Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sun, 28 Jan 2024 20:40:35 +0800 Subject: [PATCH 10/11] fix: fix uot-of-date of layered_circuit due to pos as list --- quafu/circuits/quantum_circuit.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/quafu/circuits/quantum_circuit.py b/quafu/circuits/quantum_circuit.py index 684e3cb..075e447 100644 --- a/quafu/circuits/quantum_circuit.py +++ b/quafu/circuits/quantum_circuit.py @@ -182,15 +182,11 @@ def layered_circuit(self) -> np.ndarray: gateQlist = [[] for i in range(num)] used_qubits = [] for gate in gatelist: - if ( - isinstance(gate, SingleQubitGate) - or isinstance(gate, Delay) - or isinstance(gate, QuantumPulse) - ): - gateQlist[gate.pos].append(gate) + if len(gate.pos) == 1: + p = gate.pos[0] + gateQlist[p].append(gate) if gate.pos not in used_qubits: used_qubits.append(gate.pos) - elif ( isinstance(gate, Barrier) or isinstance(gate, MultiQubitGate) @@ -202,7 +198,7 @@ def layered_circuit(self) -> np.ndarray: for j in range(pos1 + 1, pos2 + 1): gateQlist[j].append(None) - if isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance): + if isinstance(gate, (MultiQubitGate, XYResonance)): for pos in gate.pos: if pos not in used_qubits: used_qubits.append(pos) From e9be2054ede2f8920fda99efd570af0680a8959e Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sun, 28 Jan 2024 20:43:35 +0800 Subject: [PATCH 11/11] chore: union pos in ctrl __init__ --- quafu/elements/quantum_gate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quafu/elements/quantum_gate.py b/quafu/elements/quantum_gate.py index 9c10134..1cb541d 100644 --- a/quafu/elements/quantum_gate.py +++ b/quafu/elements/quantum_gate.py @@ -369,8 +369,8 @@ class ControlledGate(MultiQubitGate): def __init__( self, targ_name: str, - ctrls: PosType, - targs: PosType, + ctrls: Union[PosType, List[PosType]], + targs: Union[PosType, List[PosType]], paras: Optional[Union[ParameterType, List[ParameterType]]] = None, tar_matrix: MatrixType = None, ):