diff --git a/src/quafu/__init__.py b/src/quafu/__init__.py index 0b81913..1fbf9d9 100644 --- a/src/quafu/__init__.py +++ b/src/quafu/__init__.py @@ -4,7 +4,15 @@ from .users.userapi import User from .simulators.simulator import simulate -__all__ = ["QuantumCircuit", "ExecResult", "Task", "User", "SimuResult", "simulate", "get_version"] +__all__ = [ + "QuantumCircuit", + "ExecResult", + "Task", + "User", + "SimuResult", + "simulate", + "get_version", +] def get_version(): diff --git a/src/quafu/algorithms/ansatz.py b/src/quafu/algorithms/ansatz.py index a72a72a..9bb69ed 100644 --- a/src/quafu/algorithms/ansatz.py +++ b/src/quafu/algorithms/ansatz.py @@ -17,7 +17,7 @@ def __init__(self, pauli: str, num_layers: int = 1): def _build(self, pauli): """Construct circuit""" - gate_list = self._evol.evol(pauli, 0.) + gate_list = self._evol.evol(pauli, 0.0) for g in gate_list: self.add_gate(g) diff --git a/src/quafu/backends/backends.py b/src/quafu/backends/backends.py index ef4b831..1075ca5 100644 --- a/src/quafu/backends/backends.py +++ b/src/quafu/backends/backends.py @@ -24,11 +24,11 @@ class Backend(object): def __init__(self, backend_info: dict): - self.name = backend_info['system_name'] - self._valid_gates = backend_info['valid_gates'] - self.qubit_num = backend_info['qubits'] - self.system_id = backend_info['system_id'] - self.status = backend_info['status'] + self.name = backend_info["system_name"] + self._valid_gates = backend_info["valid_gates"] + self.qubit_num = backend_info["qubits"] + self.system_id = backend_info["system_id"] + self.status = backend_info["status"] self.qv = backend_info["QV"] # self.task_in_queue = backend_info["task_in_queue"] @@ -37,13 +37,12 @@ def get_chip_info(self, user: User = None): api_token = user.api_token data = {"system_name": self.name.lower()} headers = {"api_token": api_token} - chip_info = requests.post(url=User.chip_api, data=data, - headers=headers) + chip_info = requests.post(url=User.chip_api, data=data, headers=headers) chip_info = json.loads(chip_info.text) json_topo_struct = chip_info["topological_structure"] qubits_list = [] for gate in json_topo_struct.keys(): - qubit = gate.split('_') + qubit = gate.split("_") qubits_list.append(qubit[0]) qubits_list.append(qubit[1]) qubits_list = list(set(qubits_list)) @@ -56,14 +55,14 @@ def get_chip_info(self, user: User = None): edges_dict = {} clist = [] for gate, name_fidelity in json_topo_struct.items(): - gate_qubit = gate.split('_') + gate_qubit = gate.split("_") qubit1 = qubit_to_int[gate_qubit[0]] qubit2 = qubit_to_int[gate_qubit[1]] gate_name = list(name_fidelity.keys())[0] - fidelity = name_fidelity[gate_name]['fidelity'] + fidelity = name_fidelity[gate_name]["fidelity"] directed_weighted_edges.append([qubit1, qubit2, fidelity]) clist.append([qubit1, qubit2]) - gate_reverse = gate.split('_')[1] + '_' + gate.split('_')[0] + gate_reverse = gate.split("_")[1] + "_" + gate.split("_")[0] if gate not in edges_dict and gate_reverse not in edges_dict: edges_dict[gate] = fidelity else: @@ -72,7 +71,7 @@ def get_chip_info(self, user: User = None): edges_dict[gate] = fidelity for gate, fidelity in edges_dict.items(): - gate_qubit = gate.split('_') + gate_qubit = gate.split("_") qubit1, qubit2 = qubit_to_int[gate_qubit[0]], qubit_to_int[gate_qubit[1]] weighted_edges.append([qubit1, qubit2, np.round(fidelity, 3)]) @@ -85,8 +84,16 @@ def get_chip_info(self, user: User = None): elarge = [(u, v) for (u, v, d) in G.edges(data=True) if d["weight"] >= 0.9] esmall = [(u, v) for (u, v, d) in G.edges(data=True) if d["weight"] < 0.9] - elarge_labels = {(u, v): "%.3f" % d["weight"] for (u, v, d) in G.edges(data=True) if d["weight"] >= 0.9} - esmall_labels = {(u, v): "%.3f" % d["weight"] for (u, v, d) in G.edges(data=True) if d["weight"] < 0.9} + elarge_labels = { + (u, v): "%.3f" % d["weight"] + for (u, v, d) in G.edges(data=True) + if d["weight"] >= 0.9 + } + esmall_labels = { + (u, v): "%.3f" % d["weight"] + for (u, v, d) in G.edges(data=True) + if d["weight"] < 0.9 + } pos = nx.spring_layout(G, seed=1) fig, ax = plt.subplots() @@ -94,17 +101,25 @@ def get_chip_info(self, user: User = None): nx.draw_networkx_edges(G, pos, edgelist=elarge, width=2, ax=ax) nx.draw_networkx_edges( - G, pos, edgelist=esmall, width=2, alpha=0.5, style="dashed" - , ax=ax) + G, pos, edgelist=esmall, width=2, alpha=0.5, style="dashed", ax=ax + ) nx.draw_networkx_labels(G, pos, font_size=14, font_family="sans-serif", ax=ax) # edge_labels = nx.get_edge_attributes(G, "weight") - nx.draw_networkx_edge_labels(G, pos, elarge_labels, font_size=12, font_color="green", ax=ax) - nx.draw_networkx_edge_labels(G, pos, esmall_labels, font_size=12, font_color="red", ax=ax) + nx.draw_networkx_edge_labels( + G, pos, elarge_labels, font_size=12, font_color="green", ax=ax + ) + nx.draw_networkx_edge_labels( + G, pos, esmall_labels, font_size=12, font_color="red", ax=ax + ) fig.set_figwidth(14) fig.set_figheight(14) fig.tight_layout() - return {"mapping": int_to_qubit, "topology_diagram": fig, "full_info": chip_info} + return { + "mapping": int_to_qubit, + "topology_diagram": fig, + "full_info": chip_info, + } def get_valid_gates(self): return self._valid_gates diff --git a/src/quafu/benchmark/adder.py b/src/quafu/benchmark/adder.py index 9e4ad37..6bac7b9 100644 --- a/src/quafu/benchmark/adder.py +++ b/src/quafu/benchmark/adder.py @@ -46,26 +46,26 @@ def qreg(_i, name): qreg b[4]; qreg cout[1]; """ - if name == 'cin': + if name == "cin": return _i - elif name == 'a': + elif name == "a": return _i + 1 - elif name == 'b': + elif name == "b": return _i + 5 - elif name == 'cout': + elif name == "cout": return _i + 9 else: - raise ValueError('Unknown qreg name: {}'.format(name)) + raise ValueError("Unknown qreg name: {}".format(name)) def creg(_i, name): """ creg ans[5]; """ - if name == 'ans': + if name == "ans": return _i else: - raise ValueError('Unknown creg name: {}'.format(name)) + raise ValueError("Unknown creg name: {}".format(name)) """ @@ -73,9 +73,9 @@ def creg(_i, name): x a[0]; // a = 0001 x b; // b = 1111 """ -qc.x(qreg(0, 'a')) +qc.x(qreg(0, "a")) for i in range(4): - qc.x(qreg(i, 'b')) + qc.x(qreg(i, "b")) """ // add a to b, storing result in b @@ -89,14 +89,14 @@ def creg(_i, name): unmaj a[0],b[1],a[1]; unmaj cin[0],b[0],a[0]; """ -majority(qreg(0, 'cin'), qreg(0, 'b'), qreg(0, 'a')) -majority(qreg(0, 'a'), qreg(1, 'b'), qreg(1, 'a')) +majority(qreg(0, "cin"), qreg(0, "b"), qreg(0, "a")) +majority(qreg(0, "a"), qreg(1, "b"), qreg(1, "a")) for i in range(1, 4): - majority(qreg(i-1, 'a'), qreg(i, 'b'), qreg(i, 'a')) -qc.cnot(qreg(3, 'a'), qreg(0, 'cout')) -unmaj(qreg(2, 'a'), qreg(3, 'b'), qreg(3, 'a')) -unmaj(qreg(1, 'a'), qreg(2, 'b'), qreg(2, 'a')) -unmaj(qreg(0, 'a'), qreg(1, 'b'), qreg(1, 'a')) + majority(qreg(i - 1, "a"), qreg(i, "b"), qreg(i, "a")) +qc.cnot(qreg(3, "a"), qreg(0, "cout")) +unmaj(qreg(2, "a"), qreg(3, "b"), qreg(3, "a")) +unmaj(qreg(1, "a"), qreg(2, "b"), qreg(2, "a")) +unmaj(qreg(0, "a"), qreg(1, "b"), qreg(1, "a")) """ measure b[0] -> ans[0]; @@ -105,22 +105,23 @@ def creg(_i, name): measure b[3] -> ans[3]; measure cout[0] -> ans[4]; """ -measure_pos = [qreg(i, 'b') for i in range(4)] + [qreg(0, 'cout')] -measure_cbits = [creg(i, 'ans') for i in range(5)] +measure_pos = [qreg(i, "b") for i in range(4)] + [qreg(0, "cout")] +measure_cbits = [creg(i, "ans") for i in range(5)] qc.measure(measure_pos, cbits=measure_cbits) # qc.draw_circuit() # print(qc.to_openqasm()) init_labels = dict.fromkeys(range(n)) -init_labels[0] = 'cin' +init_labels[0] = "cin" for i in range(4): - init_labels[i+1] = f'a_{i}' + init_labels[i + 1] = f"a_{i}" for i in range(5): - init_labels[i+5] = f'b_{i}' - -end_labels = {i+5: f'ans_{i}' for i in range(5)} -qc.plot_circuit(title='Quantum ripple-carry adder', - init_labels=init_labels, - end_labels=end_labels, - ) + init_labels[i + 5] = f"b_{i}" + +end_labels = {i + 5: f"ans_{i}" for i in range(5)} +qc.plot_circuit( + title="Quantum ripple-carry adder", + init_labels=init_labels, + end_labels=end_labels, +) plt.show() diff --git a/src/quafu/benchmark/deutsch_jozsa.py b/src/quafu/benchmark/deutsch_jozsa.py index ad327f3..0f3b4ae 100644 --- a/src/quafu/benchmark/deutsch_jozsa.py +++ b/src/quafu/benchmark/deutsch_jozsa.py @@ -11,17 +11,17 @@ def get_const_oracle(qc: QuantumCircuit): output = np.random.randint(2) if output == 1: qc.x(n) - qc.name = 'Constant Oracle' + qc.name = "Constant Oracle" return qc def get_balanced_oracle(qc: QuantumCircuit): n = qc.num - 1 - b_str = ''.join([random.choice('01') for _ in range(n)]) + b_str = "".join([random.choice("01") for _ in range(n)]) # Place X-qu_gate for qubit in range(len(b_str)): - if b_str[qubit] == '1': + if b_str[qubit] == "1": qc.x(qubit) # Use barrier as divider @@ -35,10 +35,10 @@ def get_balanced_oracle(qc: QuantumCircuit): # Place X-qu_gate for qubit in range(len(b_str)): - if b_str[qubit] == '1': + if b_str[qubit] == "1": qc.x(qubit) - qc.name = 'Balanced Oracle' + qc.name = "Balanced Oracle" return qc @@ -52,12 +52,12 @@ def deutsch_jozsa(n: int, case: str): # Add oracle ################################################# - if case == 'balanced': + if case == "balanced": get_balanced_oracle(circuit) - elif case == 'constant': + elif case == "constant": get_const_oracle(circuit) else: - raise ValueError('undefined case: ' + case) + raise ValueError("undefined case: " + case) ################################################# # Repeat H-qu_gate @@ -71,5 +71,5 @@ def deutsch_jozsa(n: int, case: str): return circuit -dj_qc = deutsch_jozsa(n=4, case='constant') -dj_qc.plot_circuit(title='Deutsch-Josza Circuit', show=True) +dj_qc = deutsch_jozsa(n=4, case="constant") +dj_qc.plot_circuit(title="Deutsch-Josza Circuit", show=True) diff --git a/src/quafu/benchmark/unitary_test.py b/src/quafu/benchmark/unitary_test.py index f008c5d..7b91e8e 100644 --- a/src/quafu/benchmark/unitary_test.py +++ b/src/quafu/benchmark/unitary_test.py @@ -3,7 +3,7 @@ nqubit = 5 qubits = list(range(nqubit)) -U0 = unitary_group.rvs(2 ** nqubit) +U0 = unitary_group.rvs(2**nqubit) # Using QSD to decompose the unitary qc = QuantumCircuit(nqubit) diff --git a/src/quafu/benchmark/variational/brickwall_circuit.py b/src/quafu/benchmark/variational/brickwall_circuit.py index 8532f15..d18f082 100644 --- a/src/quafu/benchmark/variational/brickwall_circuit.py +++ b/src/quafu/benchmark/variational/brickwall_circuit.py @@ -26,9 +26,11 @@ def brickwall_layout_circuit(params, pbc=False): def plot(): para = random.random((n_layers, qubit_num, 2)) qc = brickwall_layout_circuit(para) - qc.plot_circuit(title='Brickwall Layout for \nVariational Circuit', - show=True, - save=False, - ) + qc.plot_circuit( + title="Brickwall Layout for \nVariational Circuit", + show=True, + save=False, + ) + plot() diff --git a/src/quafu/benchmark/variational/ladder_circuit.py b/src/quafu/benchmark/variational/ladder_circuit.py index ee985db..e882e66 100644 --- a/src/quafu/benchmark/variational/ladder_circuit.py +++ b/src/quafu/benchmark/variational/ladder_circuit.py @@ -23,7 +23,8 @@ def ladder_layout_circuit(params, pbc=False): def plot(): para = random.random((n_layers, bit_num)) qc = ladder_layout_circuit(para) - qc.plot_circuit(title='Ladder Layout for \nVariational Circuit', - show=True, - save=False, - ) + qc.plot_circuit( + title="Ladder Layout for \nVariational Circuit", + show=True, + save=False, + ) diff --git a/src/quafu/benchmark/variational_n4.py b/src/quafu/benchmark/variational_n4.py index f6213a6..cd13ebe 100644 --- a/src/quafu/benchmark/variational_n4.py +++ b/src/quafu/benchmark/variational_n4.py @@ -88,4 +88,4 @@ qc.from_openqasm(qasm) qc.draw_circuit() -qc.plot_circuit(show=True, title='Variational n4') +qc.plot_circuit(show=True, title="Variational n4") diff --git a/src/quafu/circuits/para_circuit.py b/src/quafu/circuits/para_circuit.py index 04a85f1..78911da 100644 --- a/src/quafu/circuits/para_circuit.py +++ b/src/quafu/circuits/para_circuit.py @@ -3,7 +3,9 @@ class QAOACircuit(QuantumCircuit): - def __init__(self, logical_qubits, physical_qubits, nodes, edges, params, p, gate="CNOT"): + def __init__( + self, logical_qubits, physical_qubits, nodes, edges, params, p, gate="CNOT" + ): num = logical_qubits self.logical_qubits = logical_qubits self.physical_qubits = physical_qubits @@ -19,8 +21,15 @@ def compile_to_IOP(self): QASM from qcover directly """ qaoa_compiler = QcoverCompiler() - self.qasm = qaoa_compiler.graph_to_qasm(self.logical_qubits, self.physical_qubits, self.nodes, self.edges, - self.paras, self.p, gate=self.gate) + self.qasm = qaoa_compiler.graph_to_qasm( + self.logical_qubits, + self.physical_qubits, + self.nodes, + self.edges, + self.paras, + self.p, + gate=self.gate, + ) def upate_paras(self, paras): self.paras = paras diff --git a/src/quafu/circuits/quantum_circuit.py b/src/quafu/circuits/quantum_circuit.py index a8b0dcc..1d4917f 100644 --- a/src/quafu/circuits/quantum_circuit.py +++ b/src/quafu/circuits/quantum_circuit.py @@ -861,7 +861,9 @@ def measure(self, pos: List[int] = None, cbits: List[int] = None) -> None: if not len(set(cbits)) == len(cbits): raise ValueError("Classical bits not uniquely assigned.") if not len(cbits) == n_num: - raise ValueError("Number of measured bits should equal to the number of classical bits") + raise ValueError( + "Number of measured bits should equal to the number of classical bits" + ) else: cbits = list(range(e_num, e_num + n_num)) diff --git a/src/quafu/circuits/quantum_register.py b/src/quafu/circuits/quantum_register.py index cc7d458..edfc03c 100644 --- a/src/quafu/circuits/quantum_register.py +++ b/src/quafu/circuits/quantum_register.py @@ -12,19 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. + class Qubit: """ Representation of logical qubits. """ - def __init__(self, logic_pos: int, reg_name: str = None, label: str = None, ): + + def __init__( + self, + logic_pos: int, + reg_name: str = None, + label: str = None, + ): self.pos = logic_pos - self.reg_name = 'q' if reg_name is None else reg_name + self.reg_name = "q" if reg_name is None else reg_name self.label = self.reg_name if label is None else label self.physical_info = None self._depth = 0 # present depth def __repr__(self): - return self.reg_name + '_%s' % self.pos + return self.reg_name + "_%s" % self.pos def load_physical_info(self, *args, **kwargs): raise NotImplementedError @@ -46,6 +53,7 @@ class QuantumRegister: """ Collection of Qubit(s) """ + def __init__(self, num: int = 0, name: str = None): self.name = name self.qubits = {i: Qubit(logic_pos=i, reg_name=name) for i in range(num)} @@ -56,9 +64,12 @@ def __getitem__(self, item): def __len__(self): return len(self.qubits) - def __add__(self, other: 'QuantumRegister'): + def __add__(self, other: "QuantumRegister"): qreg = QuantumRegister(name=self.name) - qreg.qubits = {**{self.qubits}, **{i + len(self): qubit for i, qubit in other.qubits.item()}} + qreg.qubits = { + **{self.qubits}, + **{i + len(self): qubit for i, qubit in other.qubits.item()}, + } return QuantumRegister(len(self) + len(other), name=self.name) def exchange(self, p1, p2): diff --git a/src/quafu/dagcircuits/circuit_dag.py b/src/quafu/dagcircuits/circuit_dag.py index 976faae..d309bd6 100644 --- a/src/quafu/dagcircuits/circuit_dag.py +++ b/src/quafu/dagcircuits/circuit_dag.py @@ -1,7 +1,7 @@ import numpy as np from quafu import QuantumCircuit -from quafu.elements.element_gates import * +from quafu.elements.element_gates import * from quafu.elements.quantum_element import Barrier, Delay, Measure, XYResonance from quafu.pulses.quantum_pulse import GaussianPulse, RectPulse, FlattopPulse @@ -9,19 +9,22 @@ from typing import Dict, Any, List, Union import copy -from quafu.dagcircuits.instruction_node import InstructionNode # instruction_node.py in the same folder as circuit_dag.py now -from quafu.dagcircuits.dag_circuit import DAGCircuit # dag_circuit.py in the same folder as circuit_dag.py now +from quafu.dagcircuits.instruction_node import ( + InstructionNode, +) # instruction_node.py in the same folder as circuit_dag.py now +from quafu.dagcircuits.dag_circuit import ( + DAGCircuit, +) # dag_circuit.py in the same folder as circuit_dag.py now # import pygraphviz as pgv from networkx.drawing.nx_pydot import write_dot from IPython.display import Image, SVG - # transform a gate in quantumcircuit of quafu(not include measure_gate), # into a node in the graph, with specific label. -def gate_to_node(input_gate,specific_label: str): - ''' +def gate_to_node(input_gate, specific_label: str): + """ transform a gate in quantumcircuit of quafu(not include measure_gate), into a node in the graph, with specific label. @@ -31,43 +34,56 @@ def gate_to_node(input_gate,specific_label: str): Returns: node: a node in the graph, with specific label. A GateWrapper object - - ''' + + """ import copy - gate = copy.deepcopy(input_gate) # avoid modifying the original gate + + gate = copy.deepcopy(input_gate) # avoid modifying the original gate if not isinstance(gate.pos, list): # if gate.pos is not a list, make it a list gate.pos = [gate.pos] # use getattr check 'paras' and other attributes if exist. if the attr doesn't exist,return None - gate.paras = getattr(gate, 'paras', None) or None - gate.duration = getattr(gate, 'duration', None) or None - gate.unit = getattr(gate, 'unit', None) or None - gate.channel = getattr(gate, 'channel', None) or None - gate.time_func = getattr(gate, 'time_func', None) or None - - if gate.paras and not isinstance(gate.paras, list): # if paras is True and not a list, make it a list + gate.paras = getattr(gate, "paras", None) or None + gate.duration = getattr(gate, "duration", None) or None + gate.unit = getattr(gate, "unit", None) or None + gate.channel = getattr(gate, "channel", None) or None + gate.time_func = getattr(gate, "time_func", None) or None + + if gate.paras and not isinstance( + gate.paras, list + ): # if paras is True and not a list, make it a list gate.paras = [gate.paras] - + # hashable_gate = InstructionNode(gate.name, gate.pos, gate.paras,gate.matrix,gate.duration,gate.unit, label=i) - hashable_gate = InstructionNode(gate.name, gate.pos, gate.paras,gate.duration, gate.unit,gate.channel,gate.time_func, label=specific_label) + hashable_gate = InstructionNode( + gate.name, + gate.pos, + gate.paras, + gate.duration, + gate.unit, + gate.channel, + gate.time_func, + label=specific_label, + ) return hashable_gate + # Building a DAG Graph using DAGCircuit from a QuantumCircuit -def circuit_to_dag(circuit, measure_flag = True): - ''' +def circuit_to_dag(circuit, measure_flag=True): + """ Building a DAG Graph using DAGCircui from a QuantumCircuit - + Args: circuit: a QuantumCircuit object measure_flag: whether to add measure_gate node to the dag graph - + Returns: g: a DAGCircuit object - + example: .. jupyter-execute:: - + from circuit_dag import circuit_to_dag, dag_to_circuit, draw_dag from quafu import QuantumCircuit @@ -77,68 +93,77 @@ def circuit_to_dag(circuit, measure_flag = True): circuit.cnot(0, 1) # Build the dag graph - dep_graph = circuit_to_dag(circuit) # dag graph - ''' - + dep_graph = circuit_to_dag(circuit) # dag graph + """ + # Starting Label Index i = 0 - + # A dictionary to store the last use of any qubit qubit_last_use = {} - + # g = nx.MultiDiGraph() # two nodes can have multiple edges # g = nx.DiGraph() # two nodes can only have one edge - g = DAGCircuit() # two nodes can only have one edge - - # Add the start node + g = DAGCircuit() # two nodes can only have one edge + + # Add the start node # g.add_node(-1,{"color": "green"}) g.add_nodes_from([(-1, {"color": "green"})]) - + # deepcopy the circuit to avoid modifying the original circuit # gates = copy.deepcopy(circuit.gates) # need to import copy # change to: gate = copy.deepcopy(input_gate) in gate_to_node() for gate in circuit.gates: # transform gate to node - hashable_gate = gate_to_node(gate,specific_label=i) + hashable_gate = gate_to_node(gate, specific_label=i) i += 1 - - g.add_node(hashable_gate,color="blue") - + + g.add_node(hashable_gate, color="blue") + # Add edges based on qubit_last_use; update last use for qubit in hashable_gate.pos: if qubit in qubit_last_use: - g.add_edge(qubit_last_use[qubit], hashable_gate,label=f'q{qubit}') + g.add_edge(qubit_last_use[qubit], hashable_gate, label=f"q{qubit}") else: - g.add_edge(-1, hashable_gate,label=f'q{qubit}',color="green") - + g.add_edge(-1, hashable_gate, label=f"q{qubit}", color="green") + qubit_last_use[qubit] = hashable_gate - if measure_flag: + if measure_flag: # Add measure_gate node qm = Any - qm.name = "measure" - qm.paras, qm.duration, qm.unit = [None,None,None] - qm.channel, qm.time_func = [None,None] + qm.name = "measure" + qm.paras, qm.duration, qm.unit = [None, None, None] + qm.channel, qm.time_func = [None, None] qm.pos = copy.deepcopy(circuit.measures) # circuit.measures is a dict - measure_gate = InstructionNode(qm.name, qm.pos, qm.paras, qm.duration, qm.unit, qm.channel, qm.time_func, label="m") - g.add_node(measure_gate,color="blue") + measure_gate = InstructionNode( + qm.name, + qm.pos, + qm.paras, + qm.duration, + qm.unit, + qm.channel, + qm.time_func, + label="m", + ) + g.add_node(measure_gate, color="blue") # Add edges from qubit_last_use[qubit] to measure_gate for qubit in measure_gate.pos: if qubit in qubit_last_use: - g.add_edge(qubit_last_use[qubit], measure_gate,label=f'q{qubit}') + g.add_edge(qubit_last_use[qubit], measure_gate, label=f"q{qubit}") else: - g.add_edge(-1, measure_gate,label=f'q{qubit}',color="green") + g.add_edge(-1, measure_gate, label=f"q{qubit}", color="green") qubit_last_use[qubit] = measure_gate - + # Add the end node # g.add_node(float('inf'),{"color": "red"}) - g.add_nodes_from([(float('inf'), {"color": "red"})]) - + g.add_nodes_from([(float("inf"), {"color": "red"})]) + for qubit in qubit_last_use: - g.add_edge(qubit_last_use[qubit], float('inf'),label=f'q{qubit}',color="red") - + g.add_edge(qubit_last_use[qubit], float("inf"), label=f"q{qubit}", color="red") + # update qubits_used, cbits_used, num_instruction_nodes g.update_qubits_used() g.update_cbits_used() @@ -188,17 +213,18 @@ def circuit_to_dag(circuit, measure_flag = True): "mcy": MCYGate, "mcz": MCZGate, "gaussian": GaussianPulse, - "rect" : RectPulse, - "flattop" : FlattopPulse, - "measure" : Measure + "rect": RectPulse, + "flattop": FlattopPulse, + "measure": Measure, } + def node_to_gate(gate_in_dag): """ transform gate in dag graph, to gate in circuit which can be added to circuit Args: - gate_in_dag: a node in dag graph , gate_in_dag is a GateWrapper object. + gate_in_dag: a node in dag graph , gate_in_dag is a GateWrapper object. in instruction_node, gate_in_dag.name is uppercase, gate_in_dag.pos is a list or a dict gate_transform support gate with one qubit or more qubits, not measures! and you should exculde nodes [-1 ,float('inf') , measure_gate] in dag graph @@ -212,7 +238,7 @@ def node_to_gate(gate_in_dag): qcircuit = QuantumCircuit(n) for gate in nx.topological_sort(dep_graph): - + if gate not in [-1, float('inf')]: # measure gate to do if gate.name == "measure": @@ -222,7 +248,7 @@ def node_to_gate(gate_in_dag): # use gate_transform to transform gate in dag graph to gate in circuit qcircuit.gates.append(node_to_gate(gate)) return qcircuit - + """ gate_name = gate_in_dag.name.lower() @@ -260,27 +286,26 @@ def node_to_gate(gate_in_dag): # time_func = gate_in_dag.time_func return gate_class(pos, *paras, duration, unit, channel) - + # measure node if gate_name == "measure": return gate_class(gate_in_dag.pos) - - return gate_class(*args) + return gate_class(*args) -# From DAG with Hashable Gates to quafu Gates added to circuit +# From DAG with Hashable Gates to quafu Gates added to circuit def dag_to_circuit(dep_graph, n: int): - ''' + """ From DAG with Hashable Gates to quafu Gates added to circuit - + Args: dep_graph (DAG): DAG with Hashable Gates n (int): number of qubits - + Returns: qcircuit (QuantumCircuit): quafu QuantumCircuit - + example: .. jupyter-execute:: @@ -293,19 +318,18 @@ def dag_to_circuit(dep_graph, n: int): circuit.cnot(0, 1) # Build the dag graph - dep_graph = circuit_to_dag(circuit) # dag graph - + dep_graph = circuit_to_dag(circuit) # dag graph + # use dag_to_circuit to transform dag graph to a new circuit reconstructed_circuit = dag_to_circuit(dep_graph, circuit.num) - - - ''' - + + + """ + qcircuit = QuantumCircuit(n) for gate in nx.topological_sort(dep_graph): - - if gate not in [-1, float('inf')]: + if gate not in [-1, float("inf")]: # measure gate to do if gate.name == "measure": qcircuit.measures = gate.pos @@ -315,9 +339,10 @@ def dag_to_circuit(dep_graph, n: int): qcircuit.gates.append(node_to_gate(gate)) return qcircuit + # Helper function to visualize the DAG,check the example in the docstring def draw_dag(dep_g, output_format="png"): - ''' + """ Helper function to visualize the DAG Args: @@ -335,23 +360,24 @@ def draw_dag(dep_g, output_format="png"): # directly draw SVG picture draw_dag(dep_g, output_format="svg") # save a svg picture "dag.svg" and show it in jupyter notebook - + ex2: # generate PNG picture - img_png = draw_dag(dep_g, output_format="png") + img_png = draw_dag(dep_g, output_format="png") # generate SVG picture - img_svg = draw_dag(dep_g, output_format="svg") - + img_svg = draw_dag(dep_g, output_format="svg") + # show PNG picture img_png - + # show SVG picture - img_svg + img_svg + + + """ + import pygraphviz - - ''' - import pygraphviz write_dot(dep_g, "dag.dot") G = pygraphviz.AGraph("dag.dot") G.layout(prog="dot") @@ -369,18 +395,18 @@ def draw_dag(dep_g, output_format="png"): def nodelist_to_dag(op_nodes: List[Any]) -> DAGCircuit: # Starting Label Index i = 0 - + # A dictionary to store the last use of any qubit qubit_last_use = {} - + # g = nx.MultiDiGraph() # two nodes can have multiple edges # g = nx.DiGraph() # two nodes can only have one edge - g = DAGCircuit() - - # Add the start node + g = DAGCircuit() + + # Add the start node # g.add_node(-1,{"color": "green"}) g.add_nodes_from([(-1, {"color": "green"})]) - + # deepcopy the circuit to avoid modifying the original circuit # gates = copy.deepcopy(circuit.gates) # need to import copy # change to: gate = copy.deepcopy(input_gate) in gate_to_node() @@ -388,86 +414,94 @@ def nodelist_to_dag(op_nodes: List[Any]) -> DAGCircuit: for op_node in op_nodes: # transform gate to node hashable_gate = copy.deepcopy(op_node) - g.add_node(hashable_gate,color="blue") - + g.add_node(hashable_gate, color="blue") + # Add edges based on qubit_last_use; update last use for qubit in hashable_gate.pos: if qubit in qubit_last_use: - g.add_edge(qubit_last_use[qubit], hashable_gate,label=f'q{qubit}') + g.add_edge(qubit_last_use[qubit], hashable_gate, label=f"q{qubit}") else: - g.add_edge(-1, hashable_gate,label=f'q{qubit}',color="green") - + g.add_edge(-1, hashable_gate, label=f"q{qubit}", color="green") + qubit_last_use[qubit] = hashable_gate - # Add the end node # g.add_node(float('inf'),{"color": "red"}) - g.add_nodes_from([(float('inf'), {"color": "red"})]) - + g.add_nodes_from([(float("inf"), {"color": "red"})]) + for qubit in qubit_last_use: - g.add_edge(qubit_last_use[qubit], float('inf'),label=f'q{qubit}',color="red") + g.add_edge(qubit_last_use[qubit], float("inf"), label=f"q{qubit}", color="red") # update the qubits_used, cbits_used, num_instruction_nodes g.qubits_used = g.update_qubits_used() g.cbits_used = g.update_cbits_used() g.num_instruction_nodes = g.update_num_instruction_nodes() - + return g -# nodes_qubit_mapping_dict + +# nodes_qubit_mapping_dict def nodelist_qubit_mapping_dict(nodes_list): - ''' + """ Args: nodes_list: a list of nodes Returns: nodes_qubit_mapping_dict: a dict about keys are the qubits used by the nodes and values are the new qubits - ''' + """ nodes_list_qubits_used = set() for node in nodes_list: - if hasattr(node, 'pos') and node.pos is not None: + if hasattr(node, "pos") and node.pos is not None: nodes_list_qubits_used = nodes_list_qubits_used | set(node.pos) - - mapping_pos = list(range(len(nodes_list_qubits_used))) + + mapping_pos = list(range(len(nodes_list_qubits_used))) # mapping, get a dict - nodes_qubit_mapping_dict = dict(zip(sorted(list(nodes_list_qubits_used)), mapping_pos)) + nodes_qubit_mapping_dict = dict( + zip(sorted(list(nodes_list_qubits_used)), mapping_pos) + ) nodes_qubit_mapping_dict return nodes_qubit_mapping_dict + def nodelist_qubit_mapping_dict_reverse(nodes_list): - ''' + """ Args: nodes_list: a list of nodes Returns: nodes_qubit_mapping_dict_reverse: a dict about keys are the new qubits and values are the qubits used by the nodes - ''' + """ nodes_qubit_mapping_dict = nodelist_qubit_mapping_dict(nodes_list) # reverse mapping, get a dict - nodes_qubit_mapping_dict_reverse = {value: key for key, value in nodes_qubit_mapping_dict.items()} - + nodes_qubit_mapping_dict_reverse = { + value: key for key, value in nodes_qubit_mapping_dict.items() + } + return nodes_qubit_mapping_dict_reverse + # a function to map nodes_list def nodes_list_mapping(nodes_list, nodes_qubit_mapping_dict): - ''' + """ Args: nodes_list: the nodes list of instruction nodes nodes_qubit_mapping_dict: the dict of the mapping qubits - return: + return: nodes_list_mapping: the nodes_list after mapping qubits - ''' - nodes_qubit_mapping_dict + """ + nodes_qubit_mapping_dict nodes_list_mapping = [] for node in nodes_list: node_new = copy.deepcopy(node) - if hasattr(node, 'pos') and node.pos is not None: + if hasattr(node, "pos") and node.pos is not None: if isinstance(node.pos, list): node_new.pos = [nodes_qubit_mapping_dict[qubit] for qubit in node.pos] elif isinstance(node.pos, dict): node_new.pos = {} # the values of the dict are void, so we need to copy the values from the original dict for qubit in node.pos: - node_new.pos[nodes_qubit_mapping_dict[qubit]] = copy.deepcopy(node.pos[qubit]) + node_new.pos[nodes_qubit_mapping_dict[qubit]] = copy.deepcopy( + node.pos[qubit] + ) nodes_list_mapping.append(node_new) - return nodes_list_mapping \ No newline at end of file + return nodes_list_mapping diff --git a/src/quafu/dagcircuits/dag_circuit.py b/src/quafu/dagcircuits/dag_circuit.py index f8e5e72..d109461 100644 --- a/src/quafu/dagcircuits/dag_circuit.py +++ b/src/quafu/dagcircuits/dag_circuit.py @@ -5,253 +5,296 @@ from networkx.classes.multidigraph import MultiDiGraph + class DAGCircuit(MultiDiGraph): - def __init__(self,qubits_used=None, cbits_used=None, incoming_graph_data=None, **attr): + def __init__( + self, qubits_used=None, cbits_used=None, incoming_graph_data=None, **attr + ): super().__init__(incoming_graph_data, **attr) - + if qubits_used is None: self.qubits_used = set() elif isinstance(qubits_used, set): self.qubits_used = qubits_used else: - raise ValueError('qubits_used should be a set or None') + raise ValueError("qubits_used should be a set or None") if cbits_used is None: self.cbits_used = set() elif isinstance(cbits_used, set): self.cbits_used = cbits_used else: - raise ValueError('cbits_used should be a set or None') - + raise ValueError("cbits_used should be a set or None") + # num of instruction nodes self.num_instruction_nodes = 0 - # add new methods or override existing methods here. def update_qubits_used(self): - ''' + """ qubits_used is a set of qubits used in DAGCircuit based on node -1's edges' labels, the qubits is the integer part of the label return: qubits_used: set of qubits used in DAGCircuit - ''' + """ if -1 not in self.nodes: - raise ValueError('-1 should be in DAGCircuit, please add it first') - self.qubits_used = set([int(edge[2]['label'][1:]) for edge in self.out_edges(-1, data=True)]) + raise ValueError("-1 should be in DAGCircuit, please add it first") + self.qubits_used = set( + [int(edge[2]["label"][1:]) for edge in self.out_edges(-1, data=True)] + ) return self.qubits_used - + def update_cbits_used(self): - ''' + """ cbits_used is a set of cbits used in DAGCircuit - calculated by measurenode's cbits + calculated by measurenode's cbits return: cbits_used: set of cbits used in DAGCircuit - ''' + """ for node in self.nodes: - # if node.has a attribute 'name' and node.name == 'measure' - if hasattr(node, 'name') and node.name == 'measure': - self.cbits_used = set(node.pos.values()) + # if node.has a attribute 'name' and node.name == 'measure' + if hasattr(node, "name") and node.name == "measure": + self.cbits_used = set(node.pos.values()) return self.cbits_used - + def update_num_instruction_nodes(self): - ''' + """ num_instruction_nodes is the number of instruction nodes in DAGCircuit - ''' + """ if -1 not in self.nodes: - raise ValueError('-1 should be in DAGCircuit, please add it first') - if float('inf') not in self.nodes: - raise ValueError('float("inf") should be in DAGCircuit, please add it first') + raise ValueError("-1 should be in DAGCircuit, please add it first") + if float("inf") not in self.nodes: + raise ValueError( + 'float("inf") should be in DAGCircuit, please add it first' + ) self.num_instruction_nodes = len(self.nodes) - 2 for node in self.nodes: - if hasattr(node, 'name') and node.name == 'measure': + if hasattr(node, "name") and node.name == "measure": self.num_instruction_nodes -= 1 return self.num_instruction_nodes - - def nodes_dict(self): - ''' + """ nodes_dict is a dictionary of nodes with the node label as key and the node as value. without -1 and float('inf') - ''' + """ nodes_dict = {} for node in nx.topological_sort(self): - if node != -1 and node != float('inf'): + if node != -1 and node != float("inf"): nodes_dict[node.label] = node return nodes_dict - def nodes_list(self): - ''' + """ nodes_list is a list of nodes without -1 and float('inf') - ''' + """ nodes_list = [] for node in nx.topological_sort(self): - if node != -1 and node != float('inf'): + if node != -1 and node != float("inf"): nodes_list.append(node) return nodes_list - - def node_qubits_predecessors(self, node:InstructionNode): - ''' + + def node_qubits_predecessors(self, node: InstructionNode): + """ node_qubits_predecessors is a dict of {qubits -> predecessors }of node Args: node in DAGCircuit, node should not be -1 Returns: node_qubits_predecessors: dict of {qubits -> predecessors }of node - ''' + """ # for edge in self.in_edges(node, data=True): # print(edge[0], edge[1], edge[2]) if node not in self.nodes: - raise ValueError('node should be in DAGCircuit') + raise ValueError("node should be in DAGCircuit") if node in [-1]: - raise ValueError('-1 has no predecessors') + raise ValueError("-1 has no predecessors") predecessor_nodes = [edge[0] for edge in self.in_edges(node, data=True)] - qubits_labels = [int(edge[2]['label'][1:]) for edge in self.in_edges(node, data=True)] + qubits_labels = [ + int(edge[2]["label"][1:]) for edge in self.in_edges(node, data=True) + ] node_qubits_predecessors = dict(zip(qubits_labels, predecessor_nodes)) return node_qubits_predecessors - - def node_qubits_successors(self, node:InstructionNode): - ''' + + def node_qubits_successors(self, node: InstructionNode): + """ node_qubits_successors is a dict of {qubits -> successors }of node - Args: + Args: node in DAGCircuit, node should not be float('inf') Returns: node_qubits_successors: dict of {qubits -> successors }of node - ''' + """ if node not in self.nodes: - raise ValueError('node should be in DAGCircuit') - if node in [float('inf')]: + raise ValueError("node should be in DAGCircuit") + if node in [float("inf")]: raise ValueError('float("inf") has no successors') successor_nodes = [edge[1] for edge in self.out_edges(node, data=True)] - qubits_labels = [int(edge[2]['label'][1:]) for edge in self.out_edges(node, data=True)] + qubits_labels = [ + int(edge[2]["label"][1:]) for edge in self.out_edges(node, data=True) + ] node_qubits_successors = dict(zip(qubits_labels, successor_nodes)) return node_qubits_successors - def node_qubits_inedges(self, node:InstructionNode): - ''' + def node_qubits_inedges(self, node: InstructionNode): + """ node_qubits_inedges is a dict of {qubits -> inedges }of node Args: node in DAGCircuit, node should not be -1 Returns: node_qubits_inedges: dict of {qubits -> inedges }of node - ''' + """ if node not in self.nodes: - raise ValueError('node should be in DAGCircuit') + raise ValueError("node should be in DAGCircuit") if node in [-1]: - raise ValueError('-1 has no predecessors') + raise ValueError("-1 has no predecessors") inedges = [edge for edge in self.in_edges(node)] - qubits_labels = [int(edge[2]['label'][1:]) for edge in self.in_edges(node, data=True)] + qubits_labels = [ + int(edge[2]["label"][1:]) for edge in self.in_edges(node, data=True) + ] node_qubits_inedges = dict(zip(qubits_labels, inedges)) return node_qubits_inedges - - def node_qubits_outedges(self, node:InstructionNode): - ''' + + def node_qubits_outedges(self, node: InstructionNode): + """ node_qubits_outedges is a dict of {qubits -> outedges }of node Args: node in DAGCircuit, node should not be float('inf') Returns: node_qubits_outedges: dict of {qubits -> outedges }of node - ''' + """ if node not in self.nodes: - raise ValueError('node should be in DAGCircuit') - if node in [float('inf')]: + raise ValueError("node should be in DAGCircuit") + if node in [float("inf")]: raise ValueError('float("inf") has no successors') outedges = [edge for edge in self.out_edges(node)] - qubits_labels = [int(edge[2]['label'][1:]) for edge in self.out_edges(node, data=True)] + qubits_labels = [ + int(edge[2]["label"][1:]) for edge in self.out_edges(node, data=True) + ] node_qubits_outedges = dict(zip(qubits_labels, outedges)) return node_qubits_outedges - - def remove_instruction_node(self, gate:InstructionNode): - ''' + + def remove_instruction_node(self, gate: InstructionNode): + """ remove a gate from DAGCircuit, and all edges connected to it. add new edges about qubits of removed gate between all predecessors and successors of removed gate. Args: gate: InstructionNode, gate should be in DAGCircuit, gate should not be -1 or float('inf') - ''' + """ if gate not in self.nodes: - raise ValueError('gate should be in DAGCircuit') - if gate in [-1, float('inf')]: + raise ValueError("gate should be in DAGCircuit") + if gate in [-1, float("inf")]: raise ValueError('gate should not be -1 or float("inf")') qubits_predecessors = self.node_qubits_predecessors(gate) qubits_successors = self.node_qubits_successors(gate) for qubit in gate.pos: - if qubits_predecessors[qubit] != -1 and qubits_successors[qubit] != float('inf'): - self.add_edge(qubits_predecessors[qubit], qubits_successors[qubit], label=f'q{qubit}') - elif qubits_predecessors[qubit] == -1 and qubits_successors[qubit] != float('inf'): - self.add_edge(qubits_predecessors[qubit], qubits_successors[qubit], label=f'q{qubit}',color='green') + if qubits_predecessors[qubit] != -1 and qubits_successors[qubit] != float( + "inf" + ): + self.add_edge( + qubits_predecessors[qubit], + qubits_successors[qubit], + label=f"q{qubit}", + ) + elif qubits_predecessors[qubit] == -1 and qubits_successors[qubit] != float( + "inf" + ): + self.add_edge( + qubits_predecessors[qubit], + qubits_successors[qubit], + label=f"q{qubit}", + color="green", + ) else: - self.add_edge(qubits_predecessors[qubit], qubits_successors[qubit], label=f'q{qubit}',color='red') + self.add_edge( + qubits_predecessors[qubit], + qubits_successors[qubit], + label=f"q{qubit}", + color="red", + ) self.remove_node(gate) # update qubits self.qubits_used = self.update_qubits_used() - def merge_dag(self, other_dag): - ''' + """ merge other_dag into self Args: other_dag: DAGCircuit Returns: self: DAGCircuit - ''' + """ if not isinstance(other_dag, DAGCircuit): - raise ValueError('other_dag should be a DAGCircuit') + raise ValueError("other_dag should be a DAGCircuit") if other_dag == None: return self if self == None: return other_dag - - # for the same qubits (intersection), + + # for the same qubits (intersection), # remove the outgoing edges from the final node of the original DAG and the incoming edges from the initial node of the other DAG, # then connect the corresponding tail and head nodes by adding edges other_dag_qubits_used = other_dag.update_qubits_used() self_qubits_used = self.update_qubits_used() insect_qubits = self_qubits_used & other_dag_qubits_used - end_edges_labels_1 = self.node_qubits_inedges(float('inf')) + end_edges_labels_1 = self.node_qubits_inedges(float("inf")) start_edges_labels_2 = other_dag.node_qubits_outedges(-1) if len(insect_qubits) != 0: for insect_qubit in insect_qubits: self.remove_edges_from([end_edges_labels_1[insect_qubit]]) other_dag.remove_edges_from([start_edges_labels_2[insect_qubit]]) - self.add_edge(end_edges_labels_1[insect_qubit][0], start_edges_labels_2[insect_qubit][1], label=f'q{insect_qubit}') - + self.add_edge( + end_edges_labels_1[insect_qubit][0], + start_edges_labels_2[insect_qubit][1], + label=f"q{insect_qubit}", + ) + # add other_dag's nodes and edges into self # !if we add edges, we don't need to add nodes again self.add_edges_from(other_dag.edges(data=True)) # remove the edges between -1 and float('inf') - self.remove_edges_from([edge for edge in self.edges(data=True) if edge[0] == -1 and edge[1] == float('inf')]) - - # update qubits + self.remove_edges_from( + [ + edge + for edge in self.edges(data=True) + if edge[0] == -1 and edge[1] == float("inf") + ] + ) + + # update qubits self.qubits_used = self.update_qubits_used() - - def add_instruction_node(self, gate:InstructionNode,predecessors_dict:Dict[int,InstructionNode],successors_dict:Dict[int,InstructionNode]): - ''' + + def add_instruction_node( + self, + gate: InstructionNode, + predecessors_dict: Dict[int, InstructionNode], + successors_dict: Dict[int, InstructionNode], + ): + """ add a gate into DAGCircuit, and all edges connected to it. add new edges about qubits of new gate between all predecessors and successors of new gate. Args: gate: InstructionNode, gate should not be -1 or float('inf') predecessors_dict: dict of {qubits -> predecessors }of gate successors_dict: dict of {qubits -> successors }of gate - ''' - if gate in [-1, float('inf')]: + """ + if gate in [-1, float("inf")]: raise ValueError('gate should not be -1 or float("inf")') - - #remove the edges between the predessors,successors about the qubits used by the added node + + # remove the edges between the predessors,successors about the qubits used by the added node qubits_pre_out_edges = [] qubits_suc_in_edges = [] for qubit in gate.pos: @@ -267,20 +310,23 @@ def add_instruction_node(self, gate:InstructionNode,predecessors_dict:Dict[int,I # add the new node and edges for qubit in gate.pos: if predecessors_dict[qubit] == -1: - self.add_edge(predecessors_dict[qubit], gate, label=f'q{qubit}',color='green') + self.add_edge( + predecessors_dict[qubit], gate, label=f"q{qubit}", color="green" + ) else: - self.add_edge(predecessors_dict[qubit], gate, label=f'q{qubit}') - if successors_dict[qubit] == float('inf'): - self.add_edge(gate, successors_dict[qubit], label=f'q{qubit}',color='red') + self.add_edge(predecessors_dict[qubit], gate, label=f"q{qubit}") + if successors_dict[qubit] == float("inf"): + self.add_edge( + gate, successors_dict[qubit], label=f"q{qubit}", color="red" + ) else: - self.add_edge(gate, successors_dict[qubit], label=f'q{qubit}') + self.add_edge(gate, successors_dict[qubit], label=f"q{qubit}") # update qubits self.qubits_used = self.update_qubits_used() - def is_dag(self): - ''' + """ is_dag is a bool value to check if DAGCircuit is a DAG - ''' - return nx.is_directed_acyclic_graph(self) \ No newline at end of file + """ + return nx.is_directed_acyclic_graph(self) diff --git a/src/quafu/dagcircuits/instruction_node.py b/src/quafu/dagcircuits/instruction_node.py index 3c55778..a474d7f 100644 --- a/src/quafu/dagcircuits/instruction_node.py +++ b/src/quafu/dagcircuits/instruction_node.py @@ -1,40 +1,41 @@ from typing import Dict, Any, List, Union -import dataclasses +import dataclasses + @dataclasses.dataclass -class InstructionNode: - name:Any # gate.name - pos:Union[List[Any], Dict[Any,Any]] # gate.pos | Dict[Any,Any] for measure - paras:List[Any] # gate.paras +class InstructionNode: + name: Any # gate.name + pos: Union[List[Any], Dict[Any, Any]] # gate.pos | Dict[Any,Any] for measure + paras: List[Any] # gate.paras # matrix:List[Any] # for gate in [QuantumGate] - duration: Union[float,int] # for gate in [Delay,XYResonance,QuantumPulse] in quafu - unit:str # for gate in [Delay,XYResonance] in quafu - channel:str # for gate in [QuantumPulse] in quafu - time_func: Any # for gate in [QuantumPulse] in quafu - label:str # used for specifying the instruction node - + duration: Union[float, int] # for gate in [Delay,XYResonance,QuantumPulse] in quafu + unit: str # for gate in [Delay,XYResonance] in quafu + channel: str # for gate in [QuantumPulse] in quafu + time_func: Any # for gate in [QuantumPulse] in quafu + label: str # used for specifying the instruction node + def __hash__(self): - return hash((type(self.name), tuple(self.pos) ,self.label)) - + return hash((type(self.name), tuple(self.pos), self.label)) + def __str__(self): - if self.name == 'measure': - args = ','.join(str(q) for q in self.pos.keys()) + if self.name == "measure": + args = ",".join(str(q) for q in self.pos.keys()) args += f'=>{",".join(str(c) for c in self.pos.values())}' - else: - args = ','.join(str(q) for q in self.pos) - + else: + args = ",".join(str(q) for q in self.pos) + if self.paras == None: - return f'{self.label}{{{self.name}({args})}}' # no paras + return f"{self.label}{{{self.name}({args})}}" # no paras else: # if self.paras not a list, then make it a list of str of .3f float if not isinstance(self.paras, list): - formatted_paras = [f'{self.paras:.3f}'] + formatted_paras = [f"{self.paras:.3f}"] else: - formatted_paras = [f'{p:.3f}' for p in self.paras] - - formatted_paras_str = ','.join(formatted_paras) - - return f'{self.label}{{{self.name}({args})}}({formatted_paras_str})' - - def __repr__(self): - return str(self) \ No newline at end of file + formatted_paras = [f"{p:.3f}" for p in self.paras] + + formatted_paras_str = ",".join(formatted_paras) + + return f"{self.label}{{{self.name}({args})}}({formatted_paras_str})" + + def __repr__(self): + return str(self) diff --git a/src/quafu/elements/element_gates/__init__.py b/src/quafu/elements/element_gates/__init__.py index 434d0e6..e49ce60 100644 --- a/src/quafu/elements/element_gates/__init__.py +++ b/src/quafu/elements/element_gates/__init__.py @@ -1,4 +1,15 @@ -from .pauli import XGate, YGate, ZGate, IdGate, WGate, SWGate, SXGate, SXdgGate, SYGate, SYdgGate +from .pauli import ( + XGate, + YGate, + ZGate, + IdGate, + WGate, + SWGate, + SXGate, + SXdgGate, + SYGate, + SYdgGate, +) from .clifford import HGate, SGate, SdgGate, TGate, TdgGate from .phase import PhaseGate from .rotation import RXGate, RYGate, RZGate, RXXGate, RYYGate, RZZGate @@ -9,12 +20,33 @@ from .cm1 import MCXGate, MCYGate, MCZGate, ControlledU from .unitary import UnitaryDecomposer -__all__ = ['XGate', 'YGate', 'ZGate', 'IdGate', 'WGate', 'SWGate', - 'PhaseGate', - 'RXGate', 'RYGate', 'RZGate', 'RXXGate', 'RYYGate', 'RZZGate', - 'SwapGate', 'ISwapGate', - 'CXGate', 'CYGate', 'CZGate', 'CSGate', 'CTGate', 'CPGate', - 'ToffoliGate', - 'FredkinGate', - 'MCXGate', 'MCYGate', 'MCZGate', 'ControlledU', - 'UnitaryDecomposer'] +__all__ = [ + "XGate", + "YGate", + "ZGate", + "IdGate", + "WGate", + "SWGate", + "PhaseGate", + "RXGate", + "RYGate", + "RZGate", + "RXXGate", + "RYYGate", + "RZZGate", + "SwapGate", + "ISwapGate", + "CXGate", + "CYGate", + "CZGate", + "CSGate", + "CTGate", + "CPGate", + "ToffoliGate", + "FredkinGate", + "MCXGate", + "MCYGate", + "MCZGate", + "ControlledU", + "UnitaryDecomposer", +] diff --git a/src/quafu/elements/element_gates/c21.py b/src/quafu/elements/element_gates/c21.py index 5419cbd..632b54c 100644 --- a/src/quafu/elements/element_gates/c21.py +++ b/src/quafu/elements/element_gates/c21.py @@ -10,4 +10,3 @@ def __init__(self, ctrl1: int, ctrl2: int, targ: int): ControlledGate.register_gate(ToffoliGate) - diff --git a/src/quafu/elements/element_gates/clifford.py b/src/quafu/elements/element_gates/clifford.py index ea10936..43af924 100644 --- a/src/quafu/elements/element_gates/clifford.py +++ b/src/quafu/elements/element_gates/clifford.py @@ -14,8 +14,7 @@ def __init__(self, pos: int): class SGate(FixedSingleQubitGate): name = "S" - matrix = np.array([[1., 0.], - [0., 1.j]], dtype=complex) + matrix = np.array([[1.0, 0.0], [0.0, 1.0j]], dtype=complex) def __init__(self, pos: int): super().__init__(pos) @@ -31,8 +30,7 @@ def __init__(self, pos: int): class TGate(FixedSingleQubitGate): name = "T" - matrix = np.array([[1., 0.], - [0., np.exp(1.j * np.pi / 4)]], dtype=complex) + matrix = np.array([[1.0, 0.0], [0.0, np.exp(1.0j * np.pi / 4)]], dtype=complex) def __init__(self, pos: int): super().__init__(pos) diff --git a/src/quafu/elements/element_gates/cm1.py b/src/quafu/elements/element_gates/cm1.py index 231d24b..5239068 100644 --- a/src/quafu/elements/element_gates/cm1.py +++ b/src/quafu/elements/element_gates/cm1.py @@ -26,8 +26,9 @@ def __init__(self, ctrls, targ: int): class ControlledU(ControlledGate): - """ Controlled gate class, where the matrix act non-trivially on target qubits""" - name = 'CU' + """Controlled gate class, where the matrix act non-trivially on target qubits""" + + name = "CU" def __init__(self, ctrls: List[int], u: Union[SingleQubitGate, MultiQubitGate]): self.targ_gate = u @@ -35,7 +36,9 @@ def __init__(self, ctrls: List[int], u: Union[SingleQubitGate, MultiQubitGate]): if isinstance(targs, int): targs = [targs] - super().__init__(u.name, ctrls, targs, u.paras, tar_matrix=self.targ_gate.get_targ_matrix()) + super().__init__( + u.name, ctrls, targs, u.paras, tar_matrix=self.targ_gate.get_targ_matrix() + ) def get_targ_matrix(self, reverse_order=False): return self.targ_gate.get_targ_matrix(reverse_order) diff --git a/src/quafu/elements/element_gates/matrices/mat_lib.py b/src/quafu/elements/element_gates/matrices/mat_lib.py index b40ebec..6be737d 100644 --- a/src/quafu/elements/element_gates/matrices/mat_lib.py +++ b/src/quafu/elements/element_gates/matrices/mat_lib.py @@ -1,114 +1,175 @@ import numpy as np IdMatrix = np.eye(2, dtype=complex) -XMatrix = np.array([[0., 1.], [1., 0.]], dtype=complex) -YMatrix = np.array([[0., -1.j], [1.j, 0.]], dtype=complex) -ZMatrix = np.array([[1., 0.], [0., -1.]], dtype=complex) -SMatrix = np.array([[1., 0.], [0., 1.j]], dtype=complex) -SXMatrix = np.array([[1., 1.j], [1.j, 1.]], dtype=complex) / np.sqrt(2) -SYMatrix = np.array([[1., -1.], [1., 1.]], dtype=complex) / np.sqrt(2) -TMatrix = np.array([[1., 0.], [0., np.exp(1.j * np.pi / 4)]], dtype=complex) +XMatrix = np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex) +YMatrix = np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex) +ZMatrix = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex) +SMatrix = np.array([[1.0, 0.0], [0.0, 1.0j]], dtype=complex) +SXMatrix = np.array([[1.0, 1.0j], [1.0j, 1.0]], dtype=complex) / np.sqrt(2) +SYMatrix = np.array([[1.0, -1.0], [1.0, 1.0]], dtype=complex) / np.sqrt(2) +TMatrix = np.array([[1.0, 0.0], [0.0, np.exp(1.0j * np.pi / 4)]], dtype=complex) WMatrix = (XMatrix + YMatrix) / np.sqrt(2) -SWMatrix = np.array([[0.5 + 0.5j, -np.sqrt(0.5) * 1j], - [np.sqrt(0.5), 0.5 + 0.5j]], dtype=complex) +SWMatrix = np.array( + [[0.5 + 0.5j, -np.sqrt(0.5) * 1j], [np.sqrt(0.5), 0.5 + 0.5j]], dtype=complex +) HMatrix = (XMatrix + ZMatrix) / np.sqrt(2) -SwapMatrix = np.array([[1., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 0., 0.], - [0., 0., 0., 1.]], dtype=complex) -ISwapMatrix = np.array([[1., 0., 0., 0.], - [0., 0., 1.j, 0.], - [0., 1.j, 0., 0.], - [0., 0., 0., 1.]], dtype=complex) -CXMatrix = np.array([[1., 0., 0., 0.], - [0., 1., 0., 0.], - [0., 0., 0., 1.], - [0., 0., 1., 0.]], dtype=complex) -CYMatrix = np.array([[1., 0., 0., 0.], - [0., 1., 0., 0.], - [0., 0., 0., -1.j], - [0., 0., 1.j, 0.]], dtype=complex) -CZMatrix = np.array([[1., 0., 0., 0.], - [0., 1., 0., 0.], - [0., 0., 1., 0.], - [0., 0., 0., -1.]], dtype=complex) -ToffoliMatrix = np.array([[1., 0., 0., 0., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 0., 0., 0.], - [0., 0., 1., 0., 0., 0., 0., 0.], - [0., 0., 0., 1., 0., 0., 0., 0.], - [0., 0., 0., 0., 1., 0., 0., 0.], - [0., 0., 0., 0., 0., 1., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 1.], - [0., 0., 0., 0., 0., 0., 1., 0.]], dtype=complex) -FredkinMatrix = np.array([[1., 0., 0., 0., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 0., 0., 0.], - [0., 0., 1., 0., 0., 0., 0., 0.], - [0., 0., 0., 1., 0., 0., 0., 0.], - [0., 0., 0., 0., 1., 0., 0., 0.], - [0., 0., 0., 0., 0., 0., 1., 0.], - [0., 0., 0., 0., 0., 1., 0., 0.], - [0., 0., 0., 0., 0., 0., 0., 1.]], dtype=complex) - - -def u2matrix(_phi=0., _lambda=0.): +SwapMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + dtype=complex, +) +ISwapMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0j, 0.0], + [0.0, 1.0j, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + dtype=complex, +) +CXMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0], + ], + dtype=complex, +) +CYMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, -1.0j], + [0.0, 0.0, 1.0j, 0.0], + ], + dtype=complex, +) +CZMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, -1.0], + ], + dtype=complex, +) +ToffoliMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], + ], + dtype=complex, +) +FredkinMatrix = np.array( + [ + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + ], + dtype=complex, +) + + +def u2matrix(_phi=0.0, _lambda=0.0): """OpenQASM 3.0 specification""" - return np.array([[1., np.exp(-1.j * _lambda)], - [np.exp(1.j * _phi), np.exp((_phi + _lambda) * 1.j)]], dtype=complex) + return np.array( + [ + [1.0, np.exp(-1.0j * _lambda)], + [np.exp(1.0j * _phi), np.exp((_phi + _lambda) * 1.0j)], + ], + dtype=complex, + ) -def u3matrix(_theta=0., _phi=0., _lambda=0.): +def u3matrix(_theta=0.0, _phi=0.0, _lambda=0.0): """OpenQASM 3.0 specification""" - return np.array([[np.cos(0.5 * _theta), -np.exp(_lambda * 1.j) * np.sin(0.5 * _theta)], - [np.exp(_phi * 1.j) * np.sin(0.5 * _theta), - np.exp((_phi + _lambda) * 1.j) * np.cos(0.5 * _theta)]], dtype=complex) + return np.array( + [ + [np.cos(0.5 * _theta), -np.exp(_lambda * 1.0j) * np.sin(0.5 * _theta)], + [ + np.exp(_phi * 1.0j) * np.sin(0.5 * _theta), + np.exp((_phi + _lambda) * 1.0j) * np.cos(0.5 * _theta), + ], + ], + dtype=complex, + ) def rx_mat(theta): - return np.array([[np.cos(0.5 * theta), -1.j * np.sin(0.5 * theta)], - [-1.j * np.sin(0.5 * theta), np.cos(0.5 * theta)]], dtype=complex) + return np.array( + [ + [np.cos(0.5 * theta), -1.0j * np.sin(0.5 * theta)], + [-1.0j * np.sin(0.5 * theta), np.cos(0.5 * theta)], + ], + dtype=complex, + ) def ry_mat(theta): - return np.array([[np.cos(0.5 * theta), - np.sin(0.5 * theta)], - [np.sin(0.5 * theta), np.cos(0.5 * theta)]], dtype=complex) + return np.array( + [ + [np.cos(0.5 * theta), -np.sin(0.5 * theta)], + [np.sin(0.5 * theta), np.cos(0.5 * theta)], + ], + dtype=complex, + ) def rz_mat(theta): - return np.array([[np.exp(-0.5j * theta), 0.], - [0., np.exp(0.5j * theta)]], dtype=complex) + return np.array( + [[np.exp(-0.5j * theta), 0.0], [0.0, np.exp(0.5j * theta)]], dtype=complex + ) def pmatrix(labda): - return np.array([[1, 0], - [0, np.exp(1j * labda)]], dtype=complex) + return np.array([[1, 0], [0, np.exp(1j * labda)]], dtype=complex) def rxx_mat(theta): """Unitary evolution of XX interaction""" - return np.array([[np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], - [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)] - ]) + return np.array( + [ + [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], + [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)], + ] + ) def ryy_mat(theta): - """ Unitary evolution of YY interaction""" + """Unitary evolution of YY interaction""" c = np.cos(theta / 2) s = 1j * np.sin(theta / 2) - return np.array([[c, 0, 0, +s], - [0, c, -s, 0], - [0, -s, c, 0], - [+s, 0, 0, c] - ]) + return np.array([[c, 0, 0, +s], [0, c, -s, 0], [0, -s, c, 0], [+s, 0, 0, c]]) def rzz_mat(theta): - return np.array([[np.exp(-1j * theta / 2), 0, 0, 0], - [0, np.exp(1j * theta / 2), 0, 0], - [0, 0, np.exp(1j * theta / 2), 0], - [0, 0, 0, np.exp(-1j * theta / 2)] - ]) + return np.array( + [ + [np.exp(-1j * theta / 2), 0, 0, 0], + [0, np.exp(1j * theta / 2), 0, 0], + [0, 0, np.exp(1j * theta / 2), 0], + [0, 0, 0, np.exp(-1j * theta / 2)], + ] + ) + # def su2_matrix(gamma: float, beta: float, delta: float): # """ diff --git a/src/quafu/elements/element_gates/matrices/mat_utils.py b/src/quafu/elements/element_gates/matrices/mat_utils.py index 479799c..9eb4f7d 100644 --- a/src/quafu/elements/element_gates/matrices/mat_utils.py +++ b/src/quafu/elements/element_gates/matrices/mat_utils.py @@ -93,7 +93,7 @@ def is_kron_with_id2(matrix): ####################################################### def get_global_phase(unitary): - """ Get the global phase of arbitrary unitary, and get the special unitary. + """Get the global phase of arbitrary unitary, and get the special unitary. Args: unitary (np.array): arbitrary unitary @@ -108,7 +108,7 @@ def get_global_phase(unitary): def matrix_distance_squared(unitary1, unitary2): - """ Used to compare the distance of two matrices. The global phase is ignored. + """Used to compare the distance of two matrices. The global phase is ignored. Args: unitary1 (np.array): A unitary matrix in the form of a numpy ndarray. @@ -118,4 +118,6 @@ def matrix_distance_squared(unitary1, unitary2): Float : A single value between 0 and 1 indicating how closely unitary1 and unitary2 match. A value close to 0 indicates that unitary1 and unitary2 are the same unitary. """ - return np.abs(1 - np.abs(np.sum(np.multiply(unitary1, np.conj(unitary2)))) / unitary1.shape[0]) + return np.abs( + 1 - np.abs(np.sum(np.multiply(unitary1, np.conj(unitary2)))) / unitary1.shape[0] + ) diff --git a/src/quafu/elements/element_gates/pauli.py b/src/quafu/elements/element_gates/pauli.py index 27143bf..6ce21e7 100644 --- a/src/quafu/elements/element_gates/pauli.py +++ b/src/quafu/elements/element_gates/pauli.py @@ -45,7 +45,11 @@ def __init__(self, pos: int): self.symbol = "W" def to_qasm(self): - return "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d]" % (self.pos, self.pos, self.pos) + return "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d]" % ( + self.pos, + self.pos, + self.pos, + ) class SWGate(FixedSingleQubitGate): @@ -57,13 +61,18 @@ def __init__(self, pos: int): self.symbol = "√W" def to_qasm(self): - return "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d]" % (self.pos, self.pos, self.pos) + return "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d]" % ( + self.pos, + self.pos, + self.pos, + ) class SXGate(FixedSingleQubitGate): name = "SX" - matrix = np.array([[0.5 + 0.5j, 0.5 - 0.5j], - [0.5 - 0.5j, 0.5 + 0.5j]], dtype=complex) + matrix = np.array( + [[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]], dtype=complex + ) def __init__(self, pos: int): super().__init__(pos) @@ -80,8 +89,9 @@ def __init__(self, pos: int): class SYGate(FixedSingleQubitGate): name = "SY" - matrix = np.array([[0.5 + 0.5j, -0.5 - 0.5j], - [0.5 + 0.5j, 0.5 + 0.5j]], dtype=complex) + matrix = np.array( + [[0.5 + 0.5j, -0.5 - 0.5j], [0.5 + 0.5j, 0.5 + 0.5j]], dtype=complex + ) def __init__(self, pos: int): super().__init__(pos) diff --git a/src/quafu/elements/element_gates/phase.py b/src/quafu/elements/element_gates/phase.py index f40d119..aaea761 100644 --- a/src/quafu/elements/element_gates/phase.py +++ b/src/quafu/elements/element_gates/phase.py @@ -5,7 +5,7 @@ class PhaseGate(ParaSingleQubitGate): name = "P" - def __init__(self, pos: int, paras: float = 0.): + def __init__(self, pos: int, paras: float = 0.0): super().__init__(pos, paras=paras) @property diff --git a/src/quafu/elements/element_gates/rotation.py b/src/quafu/elements/element_gates/rotation.py index 69ee7f7..6f6f4c4 100644 --- a/src/quafu/elements/element_gates/rotation.py +++ b/src/quafu/elements/element_gates/rotation.py @@ -5,7 +5,7 @@ class RXGate(ParaSingleQubitGate): name = "RX" - def __init__(self, pos: int, paras: float = 0.): + def __init__(self, pos: int, paras: float = 0.0): super().__init__(pos, paras) @property @@ -16,7 +16,7 @@ def matrix(self): class RYGate(ParaSingleQubitGate): name = "RY" - def __init__(self, pos: int, paras: float = 0.): + def __init__(self, pos: int, paras: float = 0.0): super().__init__(pos, paras) @property @@ -27,7 +27,7 @@ def matrix(self): class RZGate(ParaSingleQubitGate): name = "RZ" - def __init__(self, pos: int, paras: float = 0.): + def __init__(self, pos: int, paras: float = 0.0): super().__init__(pos, paras) @property @@ -38,7 +38,7 @@ def matrix(self): class RXXGate(ParaMultiQubitGate): name = "RXX" - def __init__(self, q1: int, q2: int, theta: float = 0.): + def __init__(self, q1: int, q2: int, theta: float = 0.0): super().__init__([q1, q2], paras=theta) @property @@ -52,7 +52,7 @@ def get_targ_matrix(self, reverse_order=False): class RYYGate(ParaMultiQubitGate): name = "RYY" - def __init__(self, q1: int, q2: int, theta: float = 0.): + def __init__(self, q1: int, q2: int, theta: float = 0.0): super().__init__([q1, q2], paras=theta) @property @@ -66,7 +66,7 @@ def get_targ_matrix(self, reverse_order=False): class RZZGate(ParaMultiQubitGate): name = "RZZ" - def __init__(self, q1: int, q2: int, theta: float = 0.): + def __init__(self, q1: int, q2: int, theta: float = 0.0): super().__init__([q1, q2], paras=theta) @property diff --git a/src/quafu/elements/element_gates/unitary/__init__.py b/src/quafu/elements/element_gates/unitary/__init__.py index 22d04e9..67c0d00 100644 --- a/src/quafu/elements/element_gates/unitary/__init__.py +++ b/src/quafu/elements/element_gates/unitary/__init__.py @@ -1,2 +1 @@ from .decomposer import UnitaryDecomposer - diff --git a/src/quafu/elements/element_gates/unitary/decomposer.py b/src/quafu/elements/element_gates/unitary/decomposer.py index ddbb42b..7c16de0 100644 --- a/src/quafu/elements/element_gates/unitary/decomposer.py +++ b/src/quafu/elements/element_gates/unitary/decomposer.py @@ -18,7 +18,9 @@ def __init__(self, array: ndarray, qubits, verbose: bool = False): self.qubits = qubits self.verbose = verbose - self.Mk_table = genMk_table(self.qubit_num) # initialize the general M^k lookup table + self.Mk_table = genMk_table( + self.qubit_num + ) # initialize the general M^k lookup table self.gate_list = [] def __call__(self, *args, **kwargs): @@ -31,7 +33,7 @@ def __call__(self, *args, **kwargs): def _check_unitary(array: ndarray, qubits): assert len(array.shape) == 2 assert mu.is_unitary(array) - assert len(array) == 2 ** qubits + assert len(array) == 2**qubits def _decompose_matrix(self, _matrix, qubits): qubit_num = len(qubits) @@ -40,9 +42,9 @@ def _decompose_matrix(self, _matrix, qubits): # self.gates_list.append((_matrix, qubits, 'U')) # ZYZ decomposition for single-qubit gate gamma, beta, alpha, global_phase = zyz_decomposition(_matrix) - self.gate_list.append((rz_mat(gamma), qubits, 'RZ', gamma)) - self.gate_list.append((ry_mat(beta), qubits, 'RY', beta)) - self.gate_list.append((rz_mat(alpha), qubits, 'RZ', alpha)) + self.gate_list.append((rz_mat(gamma), qubits, "RZ", gamma)) + self.gate_list.append((ry_mat(beta), qubits, "RY", beta)) + self.gate_list.append((rz_mat(alpha), qubits, "RZ", alpha)) return None # if num_qubit == 2: @@ -55,12 +57,16 @@ def _decompose_matrix(self, _matrix, qubits): if mu.is_zero(U01) and mu.is_zero(U10): if mu.is_approx(U00, U11): if self.verbose: - print('Optimization: Unitaries are equal, ' - 'skip one step in the recursion for unitaries of size') + print( + "Optimization: Unitaries are equal, " + "skip one step in the recursion for unitaries of size" + ) self._decompose_matrix(U00, qubits[1:]) else: if self.verbose: - print("Optimization: q2 is zero, only demultiplexing will be performed.") + print( + "Optimization: q2 is zero, only demultiplexing will be performed." + ) V, D, W = demultiplexing(U00, U11) self._decompose_matrix(W, qubits[1:]) self.multi_controlled_z(D, qubits[1:], qubits[0]) @@ -99,15 +105,15 @@ def multi_controlled_z(self, D, qubits, target_qubit): Mk = self.Mk_table[num_qubit - 1] thetas = np.linalg.solve(Mk, alphas) # print(thetas) - assert len(thetas) == 2 ** num_qubit + assert len(thetas) == 2**num_qubit index = get_multi_control_index(num_qubit) assert len(index) == len(thetas) for i in range(len(index)): control_qubit = qubits[index[i]] - self.gate_list.append((rz_mat(thetas[i]), [target_qubit], 'RZ', thetas[i])) - self.gate_list.append((CXMatrix, [control_qubit, target_qubit], 'CX')) + self.gate_list.append((rz_mat(thetas[i]), [target_qubit], "RZ", thetas[i])) + self.gate_list.append((CXMatrix, [control_qubit, target_qubit], "CX")) def multi_controlled_y(self, ss, qubits, target_qubit): assert len(qubits) == int(math.log(ss.shape[0], 2)) @@ -117,28 +123,28 @@ def multi_controlled_y(self, ss, qubits, target_qubit): Mk = self.Mk_table[num_qubit - 1] thetas = np.linalg.solve(Mk, alphas) # print(thetas) - assert len(thetas) == 2 ** num_qubit + assert len(thetas) == 2**num_qubit index = get_multi_control_index(num_qubit) assert len(index) == len(thetas) for i in range(len(index)): control_qubit = qubits[index[i]] - self.gate_list.append((ry_mat(thetas[i]), [target_qubit], 'RY', thetas[i])) - self.gate_list.append((CXMatrix, [control_qubit, target_qubit], 'CX')) + self.gate_list.append((ry_mat(thetas[i]), [target_qubit], "RY", thetas[i])) + self.gate_list.append((CXMatrix, [control_qubit, target_qubit], "CX")) def apply_to_qc(self, qc): if len(self.gate_list) == 0: self() for g in self.gate_list: - if g[2] == 'CX': + if g[2] == "CX": qc.cnot(g[1][0], g[1][1]) - elif g[2] == 'RY': + elif g[2] == "RY": qc.ry(g[1][0], g[3].real) - elif g[2] == 'RZ': + elif g[2] == "RZ": qc.rz(g[1][0], g[3].real) - elif g[2] == 'U': + elif g[2] == "U": gamma, beta, alpha, global_phase = zyz_decomposition(g[0]) qc.rz(g[1][0], gamma) qc.ry(g[1][0], beta) @@ -150,7 +156,7 @@ def apply_to_qc(self, qc): def zyz_decomposition(unitary): - """ ZYZ decomposition of arbitrary single-qubit gate (unitary). + """ZYZ decomposition of arbitrary single-qubit gate (unitary). SU = Rz(gamma) * Ry(beta) * Rz(alpha) Args: @@ -197,7 +203,7 @@ def _thin_csd(q1, q2): k = k + 1 print("the k size: {}".format(k)) - u2, _ = np.linalg.qr(q2[:, 0:k], mode='complete') + u2, _ = np.linalg.qr(q2[:, 0:k], mode="complete") # u2, _= np.linalg.qr(q2, mode='complete') print("the size of u2: {}".format(u2.shape)) # print("the u2 matrix: {}".format(u2)) @@ -216,7 +222,7 @@ def _thin_csd(q1, q2): v1d[:, k:p] = v1d[:, k:p] @ vtd w = cm[k:p, k:p] - z, r = np.linalg.qr(w, mode='complete') + z, r = np.linalg.qr(w, mode="complete") cm[k:p, k:p] = r u1[:, k:p] = u1[:, k:p] @ z @@ -315,7 +321,7 @@ def _graycode(n): def get_multi_control_index(n): gray_codes = list(_graycode(n)) - size = 2 ** n + size = 2**n index_list = [] for i in range(size): @@ -339,13 +345,12 @@ def bin2gray(num): return num ^ int((num >> 1)) def genMk(k): - - Mk = np.zeros((2 ** k, 2 ** k)) - for i in range(2 ** k): - for j in range(2 ** k): + Mk = np.zeros((2**k, 2**k)) + for i in range(2**k): + for j in range(2**k): p = i & bin2gray(j) strbin = "{0:b}".format(p) - tmp = [m.start() for m in re.finditer('1', strbin)] + tmp = [m.start() for m in re.finditer("1", strbin)] Mk[i, j] = (-1) ** len(tmp) return Mk diff --git a/src/quafu/elements/quantum_element/__init__.py b/src/quafu/elements/quantum_element/__init__.py index 7dd3936..7ec9f78 100644 --- a/src/quafu/elements/quantum_element/__init__.py +++ b/src/quafu/elements/quantum_element/__init__.py @@ -1,4 +1,12 @@ from .quantum_element import Barrier, Delay, XYResonance, Measure -from .quantum_gate import QuantumGate, SingleQubitGate, MultiQubitGate, \ - ParaSingleQubitGate, ParaMultiQubitGate, ControlledGate, FixedSingleQubitGate, FixedMultiQubitGate +from .quantum_gate import ( + QuantumGate, + SingleQubitGate, + MultiQubitGate, + ParaSingleQubitGate, + ParaMultiQubitGate, + ControlledGate, + FixedSingleQubitGate, + FixedMultiQubitGate, +) from .instruction import Instruction diff --git a/src/quafu/elements/quantum_element/instruction.py b/src/quafu/elements/quantum_element/instruction.py index e431121..f97d4db 100644 --- a/src/quafu/elements/quantum_element/instruction.py +++ b/src/quafu/elements/quantum_element/instruction.py @@ -26,13 +26,18 @@ class Instruction(ABC): @property @abstractmethod def name(self) -> str: - raise NotImplementedError('name is not implemented for %s' % self.__class__.__name__ - + ', this should never happen.') + raise NotImplementedError( + "name is not implemented for %s" % self.__class__.__name__ + + ", this should never happen." + ) @name.setter def name(self, _): import warnings - warnings.warn("Invalid assignment, names of standard instructions are not alterable.") + + warnings.warn( + "Invalid assignment, names of standard instructions are not alterable." + ) @classmethod def register_ins(cls, subclass, name: str = None): @@ -70,6 +75,3 @@ def __init__(self, pos: PosType, paras: ParaType = None, *args, **kwargs): # time_func = paras.get('time_func', None) # # return InstructionNode(name, pos, paras, duration, unit, channel, time_func, label) - - - diff --git a/src/quafu/elements/quantum_element/pulses/quantum_pulse.py b/src/quafu/elements/quantum_element/pulses/quantum_pulse.py index 1ed07c0..e90d5c1 100644 --- a/src/quafu/elements/quantum_element/pulses/quantum_pulse.py +++ b/src/quafu/elements/quantum_element/pulses/quantum_pulse.py @@ -13,13 +13,14 @@ class QuantumPulse(Instruction, ABC): pulse_classes = {} - def __init__(self, - pos: PosType, - paras: list, - duration: Union[float, int], - unit: str, - channel: str, - ): + def __init__( + self, + pos: PosType, + paras: list, + duration: Union[float, int], + unit: str, + channel: str, + ): """ Quantum Pulse for generating a quantum gate. @@ -76,12 +77,13 @@ def __str__(self): symbol += ")" return symbol - def __call__(self, - t: TimeType, - shift: Union[float, int] = 0., - offset: Union[float, int] = 0., - args: dict = None - ): + def __call__( + self, + t: TimeType, + shift: Union[float, int] = 0.0, + offset: Union[float, int] = 0.0, + args: dict = None, + ): """ Return pulse data. @@ -97,21 +99,23 @@ def __call__(self, return window * self.time_func(t - shift, **args) def __copy__(self): - """ Return a deepcopy of the pulse """ + """Return a deepcopy of the pulse""" return deepcopy(self) def to_qasm(self): return self.__str__() + " q[%d]" % self.pos - def plot(self, - t: Optional[np.ndarray] = None, - shift: Union[float, int] = 0., - offset: Union[float, int] = 0., - plot_real: bool = True, - plot_imag: bool = True, - fig=None, - ax=None, - **plot_kws): + def plot( + self, + t: Optional[np.ndarray] = None, + shift: Union[float, int] = 0.0, + offset: Union[float, int] = 0.0, + plot_real: bool = True, + plot_imag: bool = True, + fig=None, + ax=None, + **plot_kws, + ): """ Plot the pulse waveform. @@ -131,21 +135,21 @@ def plot(self, fig, ax = plt.subplots(1, 1, num=fig) pulse_data = self(t, shift=shift, offset=offset) if plot_real: - ax.plot(np.real(pulse_data), label='real', **plot_kws) + ax.plot(np.real(pulse_data), label="real", **plot_kws) if plot_imag: - ax.plot(np.imag(pulse_data), label='imag', **plot_kws) + ax.plot(np.imag(pulse_data), label="imag", **plot_kws) ax.set_xlabel("Time (%s)" % self.unit) ax.set_ylabel("Pulse Amp (a.u.)") ax.legend() plt.show() def set_pos(self, pos: int): - """ Set qubit position """ + """Set qubit position""" self.pos = pos return self def set_unit(self, unit="ns"): - """ Set duration unit """ + """Set duration unit""" self.unit = unit return self @@ -159,16 +163,18 @@ def __init__(self, pos, amp, duration, unit, channel): super().__init__(pos, [amp], duration, unit, channel) def time_func(self, t: Union[np.ndarray, float, int], **kwargs): - """ rect_time_func """ + """rect_time_func""" amp_ = kwargs["amp"] return amp_ * np.ones(np.array(t).shape) - def __call__(self, - t: TimeType, - shift: Union[float, int] = 0, - offset: Union[float, int] = 0, - *args, - **kwargs): + def __call__( + self, + t: TimeType, + shift: Union[float, int] = 0, + offset: Union[float, int] = 0, + *args, + **kwargs, + ): args = {"amp": self.amp} return super().__call__(t, shift, offset, args) @@ -183,19 +189,21 @@ def __init__(self, pos, amp, fwhm, duration, unit, channel): super().__init__(pos, [amp, fwhm], duration, unit, channel) def time_func(self, t, **kws): - """ flattop_time_func """ + """flattop_time_func""" from scipy.special import erf amp_, fwhm_ = kws["amp"], kws["fwhm"] sigma_ = fwhm_ / (2 * np.sqrt(np.log(2))) - return amp_ * (erf((self.duration - t) / sigma_) + erf(t / sigma_) - 1.) - - def __call__(self, - t: TimeType, - shift: Union[float, int] = 0, - offset: Union[float, int] = 0, - *args, - **kwargs): + return amp_ * (erf((self.duration - t) / sigma_) + erf(t / sigma_) - 1.0) + + def __call__( + self, + t: TimeType, + shift: Union[float, int] = 0, + offset: Union[float, int] = 0, + *args, + **kwargs, + ): args = {"amp": self.amp, "fwhm": self.fwhm} return super().__call__(t, shift, offset, args) @@ -214,19 +222,22 @@ def __init__(self, pos, amp, fwhm, phase, duration, unit, channel): super().__init__(pos, [amp, fwhm, phase], duration, unit, channel) def time_func(self, t, **kws): - """ gaussian_time_func """ + """gaussian_time_func""" amp_, fwhm_, phase_ = kws["amp"], kws["fwhm"], kws["phase"] # start: t = 0, center: t = 0.5 * duration, end: t = duration sigma_ = fwhm_ / np.sqrt(8 * np.log(2)) # fwhm to std. deviation return amp_ * np.exp( - -(t - 0.5 * self.duration) ** 2 / (2 * sigma_ ** 2) + 1j * phase_) - - def __call__(self, - t: TimeType, - shift: Union[float, int] = 0, - offset: Union[float, int] = 0, - *args, - **kwargs): + -((t - 0.5 * self.duration) ** 2) / (2 * sigma_**2) + 1j * phase_ + ) + + def __call__( + self, + t: TimeType, + shift: Union[float, int] = 0, + offset: Union[float, int] = 0, + *args, + **kwargs, + ): args = {"amp": self.amp, "fwhm": self.fwhm, "phase": self.phase} return super().__call__(t, shift, offset, args) diff --git a/src/quafu/elements/quantum_element/quantum_element.py b/src/quafu/elements/quantum_element/quantum_element.py index f182fbe..aa0a8c8 100644 --- a/src/quafu/elements/quantum_element/quantum_element.py +++ b/src/quafu/elements/quantum_element/quantum_element.py @@ -39,7 +39,9 @@ def __repr__(self): return f"{self.__class__.__name__}" def to_qasm(self): - return "barrier " + ",".join(["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)]) + return "barrier " + ",".join( + ["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)] + ) class Delay(Instruction): @@ -75,7 +77,8 @@ def __init__(self, qs: int, qe: int, duration: int, unit="ns"): def to_qasm(self): return "xy(%d%s) " % (self.duration, self.unit) + ",".join( - ["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)]) + ["q[%d]" % p for p in range(min(self.pos), max(self.pos) + 1)] + ) class Measure(Instruction): diff --git a/src/quafu/exceptions/__init__.py b/src/quafu/exceptions/__init__.py index 0490b03..d86e200 100644 --- a/src/quafu/exceptions/__init__.py +++ b/src/quafu/exceptions/__init__.py @@ -1,2 +1 @@ from .quafu_error import * - diff --git a/src/quafu/exceptions/circuit_error.py b/src/quafu/exceptions/circuit_error.py index 8dda42f..d102a18 100644 --- a/src/quafu/exceptions/circuit_error.py +++ b/src/quafu/exceptions/circuit_error.py @@ -3,17 +3,18 @@ # TODO class IndexOutOfRangeError(CircuitError): - """ """ + """ """ + pass class InvalidParaError(CircuitError): - """ """ + """ """ + pass class UnsupportedYet(CircuitError): - """ Un-supported instructions occurs""" - pass - + """Un-supported instructions occurs""" + pass diff --git a/src/quafu/exceptions/quafu_error.py b/src/quafu/exceptions/quafu_error.py index 221a7f8..d18e8af 100644 --- a/src/quafu/exceptions/quafu_error.py +++ b/src/quafu/exceptions/quafu_error.py @@ -18,14 +18,17 @@ def __str__(self): class CircuitError(QuafuError): """Exceptions for errors raised while building circuit.""" + pass class ServerError(QuafuError): - """ Exceptions for errors raised while connecting to server.""" + """Exceptions for errors raised while connecting to server.""" + pass class CompileError(QuafuError): - """ Exceptions for errors raised while compiling circuit. """ + """Exceptions for errors raised while compiling circuit.""" + pass diff --git a/src/quafu/qfasm/qfasm_convertor.py b/src/quafu/qfasm/qfasm_convertor.py index 9ccebce..d79d944 100644 --- a/src/quafu/qfasm/qfasm_convertor.py +++ b/src/quafu/qfasm/qfasm_convertor.py @@ -5,7 +5,7 @@ def qasm_to_circuit(qasm): - parser = QfasmParser() + parser = QfasmParser() nodes = parser.parse(qasm) n = 0 @@ -26,4 +26,3 @@ def qasm_to_circuit(qasm): q.openqasm = qasm q.measures = measures return q - diff --git a/src/quafu/qfasm/qfasm_parser.py b/src/quafu/qfasm/qfasm_parser.py index b62657c..9f97974 100644 --- a/src/quafu/qfasm/qfasm_parser.py +++ b/src/quafu/qfasm/qfasm_parser.py @@ -142,33 +142,33 @@ def p_arg_list_1(self, p): p[0] = p[1] def p_gate_like_0(self, p): - ''' + """ gate : id qubit_list - ''' + """ p[0] = InstructionNode(p[1], p[2], None, None, None, "", None, "") def p_gate_like_1(self, p): - ''' + """ gate : id '(' arg_list ')' qubit_list - ''' + """ p[0] = InstructionNode(p[1], p[5], p[3], None, None, "", None, "") def p_pulse_like_0(self, p): - ''' + """ pulse : id '(' time ',' arg_list ')' qubit_list - ''' + """ p[0] = InstructionNode(p[1], p[7], p[5], p[3][0], p[3][1], "", None, "") def p_measure_0(self, p): - ''' + """ measure : MEASURE bitreg ASSIGN bitreg - ''' + """ p[0] = InstructionNode("measure", {p[2]: p[4]}, None, None, None, "", None, "") def p_pulse_like_1(self, p): - ''' + """ pulse : id '(' time ',' arg_list ',' channel ')' qubit_list - ''' + """ p[0] = InstructionNode(p[1], p[9], p[5], p[3][0], p[3][1], p[7], None, "") def p_bitreg(self, p): @@ -209,7 +209,7 @@ def p_expression_none(self, p): p[0] = p[1] def p_expression_term(self, p): - 'expression : term' + "expression : term" p[0] = p[1] def p_expression_m(self, p): @@ -225,40 +225,40 @@ def p_term_factor(self, p): p[0] = p[1] def p_binary_operators(self, p): - '''expression : expression '+' term + """expression : expression '+' term | expression '-' term term : term '*' factor - | term '/' factor''' - if p[2] == '+': + | term '/' factor""" + if p[2] == "+": p[0] = p[1] + p[3] - elif p[2] == '-': + elif p[2] == "-": p[0] = p[1] - p[3] - elif p[2] == '*': + elif p[2] == "*": p[0] = p[1] * p[3] - elif p[2] == '/': + elif p[2] == "/": p[0] = p[1] / p[3] def p_factor_0(self, p): - ''' + """ factor : FLOAT | INT - ''' + """ p[0] = p[1] def p_factor_1(self, p): - ''' + """ factor : '(' expression ')' - ''' + """ p[0] = p[2] def p_factor_pi(self, p): - ''' + """ factor : PI - ''' + """ p[0] = np.pi def p_id(self, p): - ''' id : ID''' + """id : ID""" p[0] = p[1] def p_error(self, p): diff --git a/src/quafu/qfasm/qfasmlex.py b/src/quafu/qfasm/qfasmlex.py index 797987c..a12497e 100644 --- a/src/quafu/qfasm/qfasmlex.py +++ b/src/quafu/qfasm/qfasmlex.py @@ -1,6 +1,6 @@ -# Qfasm is modified OPENQASM 2.0. Currently it is mainly used to -# transfer circuit data to backends. Further it will -# support more instructions (classical or quantum) to enable +# Qfasm is modified OPENQASM 2.0. Currently it is mainly used to +# transfer circuit data to backends. Further it will +# support more instructions (classical or quantum) to enable # interaction with quantum hardware import ply.lex as lex @@ -8,7 +8,6 @@ class QfasmLexer(object): - def __init__(self): self.build() @@ -27,21 +26,21 @@ def token(self): "qreg": "QREG", "pi": "PI", "measure": "MEASURE", - "include": "INCLUDE" + "include": "INCLUDE", } tokens = [ - "FLOAT", - "INT", - "STRING", - "ASSIGN", - "MATCHES", - "ID", - "UNIT", - "CHANNEL", - "OPENQASM", - "NONE" - ] + list(reserved.values()) + "FLOAT", + "INT", + "STRING", + "ASSIGN", + "MATCHES", + "ID", + "UNIT", + "CHANNEL", + "OPENQASM", + "NONE", + ] + list(reserved.values()) def t_FLOAT(self, t): r"(([1-9]\d*\.\d*)|(0\.\d*[1-9]\d*))" @@ -54,7 +53,7 @@ def t_INT(self, t): return t def t_STRING(self, t): - r"\"([^\\\"]|\\.)*\"" + r"\"([^\\\"]|\\.)*\" " return t def t_ASSIGN(self, t): diff --git a/src/quafu/results/results.py b/src/quafu/results/results.py index e78a2b9..f5fc671 100644 --- a/src/quafu/results/results.py +++ b/src/quafu/results/results.py @@ -7,6 +7,7 @@ class Result(object): """Basis class for quantum results""" + pass @@ -22,10 +23,16 @@ class ExecResult(Result): """ def __init__(self, input_dict, measures): - status_map = {0: "In Queue", 1: "Running", 2: "Completed", "Canceled": 3, 4: "Failed"} + status_map = { + 0: "In Queue", + 1: "Running", + 2: "Completed", + "Canceled": 3, + 4: "Failed", + } self.measures = measures self.task_status = status_map[input_dict["status"]] - self.res = eval(input_dict['res']) + self.res = eval(input_dict["res"]) self.counts = OrderedDict(sorted(self.res.items(), key=lambda s: s[0])) self.logicalq_res = {} cbits = list(self.measures.values()) @@ -35,10 +42,11 @@ def __init__(self, input_dict, measures): newkey = "".join([key[i] for i in squeezed_cbits]) self.logicalq_res[newkey] = values - self.taskid = input_dict['task_id'] - self.taskname = input_dict['task_name'] + self.taskid = input_dict["task_id"] + self.taskname = input_dict["task_name"] self.transpiled_openqasm = input_dict["openqasm"] from ..circuits.quantum_circuit import QuantumCircuit + self.transpiled_circuit = QuantumCircuit(0) self.transpiled_circuit.from_openqasm(self.transpiled_openqasm) self.measure_base = [] @@ -88,7 +96,9 @@ def __init__(self, input, input_form): elif input_form == "state_vector": self.state_vector = input - def plot_probabilities(self, full: bool = False, reverse_basis: bool = False, sort: bool = None): + def plot_probabilities( + self, full: bool = False, reverse_basis: bool = False, sort: bool = None + ): """ Plot the probabilities from simulated results, ordered in big endian convention. @@ -174,7 +184,10 @@ def merge_measure(obslist): interset, intobsi, intbasei = intersec(obs[1], measure_base[1]) diffset, diffobsi = diff(obs[1], measure_base[1]) if not len(interset) == 0: - if all(np.array(list(obs[0]))[intobsi] == np.array(list(measure_base[0]))[intbasei]): + if all( + np.array(list(obs[0]))[intobsi] + == np.array(list(measure_base[0]))[intbasei] + ): measure_base[0] += "".join(np.array(list(obs[0]))[diffobsi]) measure_base[1].extend(diffset) targ_basis.append(mi) diff --git a/src/quafu/simulators/__init__.py b/src/quafu/simulators/__init__.py index 0519ecb..e69de29 100644 --- a/src/quafu/simulators/__init__.py +++ b/src/quafu/simulators/__init__.py @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/quafu/simulators/default_simulator.py b/src/quafu/simulators/default_simulator.py index 4db06c1..1c060b7 100644 --- a/src/quafu/simulators/default_simulator.py +++ b/src/quafu/simulators/default_simulator.py @@ -3,7 +3,14 @@ from typing import Iterable, List, Union from quafu.circuits.quantum_circuit import QuantumCircuit from ..results.results import SimuResult -from ..elements.quantum_element import Barrier, Delay, QuantumGate, SingleQubitGate, MultiQubitGate, XYResonance +from ..elements.quantum_element import ( + Barrier, + Delay, + QuantumGate, + SingleQubitGate, + MultiQubitGate, + XYResonance, +) import numpy as np from functools import reduce from sparse import COO, SparseArray @@ -18,7 +25,7 @@ def global_op(gate: QuantumGate, global_qubits: List) -> coo_matrix: if isinstance(gate, SingleQubitGate): local_mat = coo_matrix(gate.matrix) pos = global_qubits.index(gate.pos) - local_mat = kron(kron(eye(2 ** pos), local_mat), eye(2 ** (num - pos - 1))) + local_mat = kron(kron(eye(2**pos), local_mat), eye(2 ** (num - pos - 1))) return local_mat elif isinstance(gate, MultiQubitGate): @@ -33,12 +40,13 @@ def global_op(gate: QuantumGate, global_qubits: List) -> coo_matrix: new_order = np.argsort(origin_order) center_mat = COO.from_scipy_sparse(center_mat) center_mat = permutebits(center_mat, new_order).to_scipy_sparse() - center_mat = kron(kron(eye(2 ** num_left), center_mat), eye(2 ** num_right)) + center_mat = kron(kron(eye(2**num_left), center_mat), eye(2**num_right)) return center_mat -def permutebits(mat: Union[SparseArray, np.ndarray], order: Iterable) \ - -> Union[SparseArray, np.ndarray]: +def permutebits( + mat: Union[SparseArray, np.ndarray], order: Iterable +) -> Union[SparseArray, np.ndarray]: """permute qubits for operators or states""" num = len(order) order = np.array(order) @@ -46,7 +54,7 @@ def permutebits(mat: Union[SparseArray, np.ndarray], order: Iterable) \ mat = np.reshape(mat, [2] * r * num) order = np.concatenate([order + len(order) * i for i in range(r)]) mat = np.transpose(mat, order) - mat = np.reshape(mat, [2 ** num] * r) + mat = np.reshape(mat, [2**num] * r) return mat @@ -68,31 +76,35 @@ def ptrace(psi, ind_A: List, diag: bool = True) -> np.ndarray: return rho -def py_simulate(qc: QuantumCircuit, - state_ini: np.ndarray = np.array([]), - output: str = "amplitudes") -> SimuResult: +def py_simulate( + qc: QuantumCircuit, state_ini: np.ndarray = np.array([]), output: str = "amplitudes" +) -> SimuResult: """Simulate quantum circuit - Args: - qc: quantum circuit need to be simulated. - state_ini (numpy.ndarray): Input state vector - output (str): `"amplitudes"`: Return ampliteds on measured qubits. - `"density_matrix"`: Return reduced density_amtrix on measured qubits. - `"state_vector`: Return full statevector. - Returns: - SimuResult object that contain the results. + Args: + qc: quantum circuit need to be simulated. + state_ini (numpy.ndarray): Input state vector + output (str): `"amplitudes"`: Return ampliteds on measured qubits. + `"density_matrix"`: Return reduced density_amtrix on measured qubits. + `"state_vector`: Return full statevector. + Returns: + SimuResult object that contain the results. """ used_qubits = qc.used_qubits num = len(used_qubits) if not state_ini: - psi = np.zeros(2 ** num) + psi = np.zeros(2**num) psi[0] = 1 else: psi = state_ini for gate in qc.gates: - if not ((isinstance(gate, Delay)) or (isinstance(gate, Barrier)) or isinstance(gate, XYResonance)): + if not ( + (isinstance(gate, Delay)) + or (isinstance(gate, Barrier)) + or isinstance(gate, XYResonance) + ): op = global_op(gate, used_qubits) psi = op @ psi diff --git a/src/quafu/simulators/simulator.py b/src/quafu/simulators/simulator.py index 7edd68d..02fe3ef 100644 --- a/src/quafu/simulators/simulator.py +++ b/src/quafu/simulators/simulator.py @@ -7,17 +7,19 @@ from ..exceptions import QuafuError -def simulate(qc: Union[QuantumCircuit, str], - psi: np.ndarray = np.array([]), - simulator: str = "qfvm_circ", - output: str = "probabilities", - use_gpu: bool = False, - use_custatevec: bool = False) -> SimuResult: +def simulate( + qc: Union[QuantumCircuit, str], + psi: np.ndarray = np.array([]), + simulator: str = "qfvm_circ", + output: str = "probabilities", + use_gpu: bool = False, + use_custatevec: bool = False, +) -> SimuResult: """Simulate quantum circuit Args: qc: quantum circuit or qasm string that need to be simulated. psi : Input state vector - simulator:`"qfvm_circ"`: The high performance C++ circuit simulator with optional GPU support. + simulator:`"qfvm_circ"`: The high performance C++ circuit simulator with optional GPU support. `"py_simu"`: Python implemented simulator by sparse matrix with low performace for large scale circuit. `"qfvm_qasm"`: The high performance C++ qasm simulator with limited gate set. @@ -28,8 +30,7 @@ def simulate(qc: Union[QuantumCircuit, str], use_custatevec: Use cuStateVec-based `qfvm_circ` simulator. The argument `use_gpu` must also be True. Returns: - SimuResult object that contain the results. -""" + SimuResult object that contain the results.""" qasm = "" if simulator == "qfvm_qasm": if not isinstance(qc, str): @@ -87,4 +88,6 @@ def simulate(qc: Union[QuantumCircuit, str], return SimuResult(psi, output) else: - raise ValueError("output should in be 'density_matrix', 'probabilities', or 'state_vector'") + raise ValueError( + "output should in be 'density_matrix', 'probabilities', or 'state_vector'" + ) diff --git a/src/quafu/tasks/tasks.py b/src/quafu/tasks/tasks.py index 8390525..ffadf7c 100644 --- a/src/quafu/tasks/tasks.py +++ b/src/quafu/tasks/tasks.py @@ -52,14 +52,18 @@ def __init__(self, user: User = None): self.runtime_job_id = "" self.submit_history = {} self._available_backends = self.user.get_available_backends(print_info=False) - self.backend = self._available_backends[list(self._available_backends.keys())[0]] - - def config(self, - backend: str = "ScQ-P10", - shots: int = 1000, - compile: bool = True, - tomo: bool = False, - priority: int = 2) -> None: + self.backend = self._available_backends[ + list(self._available_backends.keys())[0] + ] + + def config( + self, + backend: str = "ScQ-P10", + shots: int = 1000, + compile: bool = True, + tomo: bool = False, + priority: int = 2, + ) -> None: """ Configure the task properties @@ -71,8 +75,10 @@ def config(self, priority: Task priority. """ if backend not in self._available_backends.keys(): - raise UserError("backend %s is not valid, available backends are " % backend + ", ".join( - self._available_backends.keys())) + raise UserError( + "backend %s is not valid, available backends are " % backend + + ", ".join(self._available_backends.keys()) + ) self.backend = self._available_backends[backend] self.shots = shots @@ -97,10 +103,9 @@ def get_backend_info(self) -> Dict: """ return self.backend.get_chip_info(self.user) - def submit(self, - qc: QuantumCircuit, - obslist: List = []) \ - -> Tuple[List[ExecResult], List[int]]: + def submit( + self, qc: QuantumCircuit, obslist: List = [] + ) -> Tuple[List[ExecResult], List[int]]: """ Execute the circuit with observable expectation measurement task. Args: @@ -132,7 +137,9 @@ def submit(self, for obs in obslist: for p in obs[1]: if p not in measures: - raise CircuitError("Qubit %d in observer %s is not measured." % (p, obs[0])) + raise CircuitError( + "Qubit %d in observer %s is not measured." % (p, obs[0]) + ) measure_basis, targlist = merge_measure(obslist) print("Job start, need measured in ", measure_basis) @@ -151,9 +158,7 @@ def submit(self, return exec_res, measure_results - def run(self, - qc: QuantumCircuit, - measure_base: List = None) -> ExecResult: + def run(self, qc: QuantumCircuit, measure_base: List = None) -> ExecResult: """Single run for measurement task. Args: @@ -162,7 +167,7 @@ def run(self, """ if measure_base is None: res = self.send(qc) - res.measure_base = '' + res.measure_base = "" else: for base, pos in zip(measure_base[0], measure_base[1]): @@ -176,12 +181,13 @@ def run(self, return res - def send(self, - qc: QuantumCircuit, - name: str = "", - group: str = "", - wait: bool = True, - ) -> ExecResult: + def send( + self, + qc: QuantumCircuit, + name: str = "", + group: str = "", + wait: bool = True, + ) -> ExecResult: """ Run the circuit on experimental device. @@ -194,38 +200,58 @@ def send(self, ExecResult object that contain the dict return from quantum device. """ from quafu import get_version + version = get_version() if qc.num > self.backend.qubit_num: - raise CircuitError("The qubit number %d is too large for backend %s which has %d qubits" % ( - qc.num, self.backend.name, self.backend.qubit_num)) + raise CircuitError( + "The qubit number %d is too large for backend %s which has %d qubits" + % (qc.num, self.backend.name, self.backend.qubit_num) + ) self.check_valid_gates(qc) qc.to_openqasm() - data = {"qtasm": qc.openqasm, "shots": self.shots, "qubits": qc.num, "scan": 0, - "tomo": int(self.tomo), "selected_server": self.backend.system_id, - "compile": int(self.compile), "priority": self.priority, "task_name": name, - "pyquafu_version": version, "runtime_job_id": self.runtime_job_id} + data = { + "qtasm": qc.openqasm, + "shots": self.shots, + "qubits": qc.num, + "scan": 0, + "tomo": int(self.tomo), + "selected_server": self.backend.system_id, + "compile": int(self.compile), + "priority": self.priority, + "task_name": name, + "pyquafu_version": version, + "runtime_job_id": self.runtime_job_id, + } if wait: url = User.exec_api else: url = User.exec_async_api - logging.debug('quantum circuit validated, sending task...') - headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.user.api_token} + logging.debug("quantum circuit validated, sending task...") + headers = { + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + "api_token": self.user.api_token, + } data = parse.urlencode(data) data = data.replace("%27", "'") - response = requests.post(url, headers=headers, data=data) # type: requests.models.Response + response = requests.post( + url, headers=headers, data=data + ) # type: requests.models.Response # TODO: completing status code checks # assert response.ok if response.status_code == 502: - logging.critical("Received a 502 Bad Gateway response. Please try again later.\n" - "If there is persistent failure, please report it on our github page.") + logging.critical( + "Received a 502 Bad Gateway response. Please try again later.\n" + "If there is persistent failure, please report it on our github page." + ) raise UserError() else: res_dict = response.json() import pprint + pprint.pprint(res_dict) if response.status_code in [201, 205]: raise UserError(res_dict["message"]) @@ -255,21 +281,25 @@ def retrieve(self, taskid: str) -> ExecResult: data = {"task_id": taskid} url = User.exec_recall_api - headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.user.api_token} + headers = { + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + "api_token": self.user.api_token, + } response = requests.post(url, headers=headers, data=data) res_dict = response.json() measures = eval(res_dict["measure"]) if measures is None: - raise Exception("Measure info returned is None. This may be the error under repairing." - " See https://github.com/ScQ-Cloud/pyquafu/issues/50") + raise Exception( + "Measure info returned is None. This may be the error under repairing." + " See https://github.com/ScQ-Cloud/pyquafu/issues/50" + ) return ExecResult(res_dict, measures) - def retrieve_group(self, - group: str, - history: Dict = None, - verbose: bool = True) -> List[ExecResult]: + def retrieve_group( + self, group: str, history: Dict = None, verbose: bool = True + ) -> List[ExecResult]: """ Retrieve the results of submited task by group name. @@ -287,14 +317,25 @@ def retrieve_group(self, if verbose: group = group if group else "Untitled group" print("Group: ", group) - print((" " * 5).join(["task_id".ljust(16), "task_name".ljust(10), "status".ljust(10)])) + print( + (" " * 5).join( + ["task_id".ljust(16), "task_name".ljust(10), "status".ljust(10)] + ) + ) for taskid in taskids: res = self.retrieve(taskid) taskname = res.taskname if verbose: taskname = taskname if taskname else "Untitled" - print((" " * 5).join( - [("%s" % res.taskid).ljust(16), ("%s" % taskname).ljust(10), ("%s" % res.task_status).ljust(10)])) + print( + (" " * 5).join( + [ + ("%s" % res.taskid).ljust(16), + ("%s" % taskname).ljust(10), + ("%s" % res.task_status).ljust(10), + ] + ) + ) if res.task_status == "Completed": group_res.append(res) @@ -310,7 +351,10 @@ def check_valid_gates(self, qc: QuantumCircuit) -> None: valid_gates = self.backend.get_valid_gates() for gate in qc.gates: if gate.name.lower() not in valid_gates: - raise CircuitError("Invalid operations '%s' for backend '%s'" % (gate.name, self.backend.name)) + raise CircuitError( + "Invalid operations '%s' for backend '%s'" + % (gate.name, self.backend.name) + ) else: if self.backend.name == "ScQ-S41": @@ -318,4 +362,7 @@ def check_valid_gates(self, qc: QuantumCircuit) -> None: if self.backend.name == "ScQ-P136": for gate in qc.gates: if gate.name.lower() in ["xy"]: - raise CircuitError("Invalid operations '%s' for backend '%s'" % (gate.name, self.backend.name)) + raise CircuitError( + "Invalid operations '%s' for backend '%s'" + % (gate.name, self.backend.name) + ) diff --git a/src/quafu/users/userapi.py b/src/quafu/users/userapi.py index 093469c..94f10f8 100644 --- a/src/quafu/users/userapi.py +++ b/src/quafu/users/userapi.py @@ -52,8 +52,10 @@ def __init__(self, api_token: str = None, token_dir: str = None): @property def api_token(self): if self._api_token is None: - raise APITokenNotFound(f"API token not set, neither found at dir: '{self.token_dir}'. " - "Please set up by providing api_token/token_dir when initializing User.") + raise APITokenNotFound( + f"API token not set, neither found at dir: '{self.token_dir}'. " + "Please set up by providing api_token/token_dir when initializing User." + ) return self._api_token def save_apitoken(self, apitoken=None): @@ -62,10 +64,12 @@ def save_apitoken(self, apitoken=None): """ if apitoken is not None: import warnings - warnings.warn("The argument 'apitoken' in this function will be deprecated " - "in the future, please set api token by providing 'api_token' " - "or 'token_dir' when initialize User()." - ) + + warnings.warn( + "The argument 'apitoken' in this function will be deprecated " + "in the future, please set api token by providing 'api_token' " + "or 'token_dir' when initialize User()." + ) self._api_token = apitoken file_dir = self.token_dir @@ -106,12 +110,22 @@ def get_available_backends(self, print_info=True): Get available backends """ from quafu.backends.backends import Backend + backends_info = self._get_backends_info() - self._available_backends = {info["system_name"]: Backend(info) for info in backends_info} + self._available_backends = { + info["system_name"]: Backend(info) for info in backends_info + } if print_info: print("\t ".join(["system_name".ljust(10), "qubits".ljust(5), "status"])) for backend in self._available_backends.values(): - print("\t ".join( - [backend.name.ljust(10), str(backend.qubit_num).ljust(5), backend.status])) + print( + "\t ".join( + [ + backend.name.ljust(10), + str(backend.qubit_num).ljust(5), + backend.status, + ] + ) + ) return self._available_backends diff --git a/src/quafu/utils/basis.py b/src/quafu/utils/basis.py index 71856f7..122ec02 100644 --- a/src/quafu/utils/basis.py +++ b/src/quafu/utils/basis.py @@ -1,28 +1,32 @@ import numpy as np + def get_basis(ind, N): basisstr = bin(int(ind))[2:] basisstr = basisstr.zfill(N) - basis = [int(i) for i in basisstr] + basis = [int(i) for i in basisstr] return np.array(basis) + def get_ind(basis): - biconv = 2**np.arange(len(basis)) + biconv = 2 ** np.arange(len(basis)) ind = np.dot(basis, biconv[::-1].T) return int(ind) - + + def reduce_probs(bitsA, res): - """The reduced probabilities from frequency """ - dim = 2**(len(bitsA)) + """The reduced probabilities from frequency""" + dim = 2 ** (len(bitsA)) probs = np.zeros(dim) for basestr in res: basis = np.array([int(i) for i in basestr]) ind = get_ind(basis[bitsA]) probs[ind] += res[basestr] - - probs = probs/np.sum(probs) + + probs = probs / np.sum(probs) return probs + def measure_obs(bits, res): n = len(bits) baseobs = get_baselocal(n) @@ -30,12 +34,13 @@ def measure_obs(bits, res): result = np.dot(prob_r, baseobs) return result + def get_baselocal(n): NA = n basisN = int(2**NA) baseobs = np.zeros(basisN) for i in range(basisN): basisA = get_basis(i, NA) - baseobs[i] = (-1)**(np.sum(basisA)) + baseobs[i] = (-1) ** (np.sum(basisA)) - return baseobs \ No newline at end of file + return baseobs diff --git a/src/quafu/utils/paulis.py b/src/quafu/utils/paulis.py index 39c9d62..f5bb599 100644 --- a/src/quafu/utils/paulis.py +++ b/src/quafu/utils/paulis.py @@ -2,27 +2,31 @@ from functools import reduce import sparse -si = sparse.COO(np.array([[1., 0.], - [0., 1.]],dtype=complex)) +si = sparse.COO(np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex)) -sx = sparse.COO(np.array([[0., 1.], - [1., 0.]], dtype=complex)) +sx = sparse.COO(np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)) -sy = sparse.COO(np.array([[0., -1.j], - [1.j, 0.]], dtype=complex)) +sy = sparse.COO(np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex)) -sz = sparse.COO(np.array([[1., 0.], - [0., -1.]], dtype=complex)) +sz = sparse.COO(np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)) + +spin = [np.array([1.0, 0.0]), np.array([0.0, 1.0])] -spin = [np.array([1., 0.]), np.array([0., 1.])] def rx(phi): - return np.array([[np.cos(phi / 2), -1j * np.sin(phi / 2)], - [-1j * np.sin(phi / 2), np.cos(phi / 2)]]) - + return np.array( + [ + [np.cos(phi / 2), -1j * np.sin(phi / 2)], + [-1j * np.sin(phi / 2), np.cos(phi / 2)], + ] + ) + + def ry(phi): - return np.array([[np.cos(phi / 2), -np.sin(phi / 2)], - [np.sin(phi / 2), np.cos(phi / 2)]]) + return np.array( + [[np.cos(phi / 2), -np.sin(phi / 2)], [np.sin(phi / 2), np.cos(phi / 2)]] + ) + def tensorl(ml): return reduce(sparse.kron, ml, 1) @@ -47,4 +51,4 @@ def Nbit_single(N): op_list[n] = sz sz_list.append(tensorl(op_list)) - return sx_list, sy_list, sz_list \ No newline at end of file + return sx_list, sy_list, sz_list diff --git a/src/quafu/utils/platform.py b/src/quafu/utils/platform.py index 6d479b2..851c4f6 100644 --- a/src/quafu/utils/platform.py +++ b/src/quafu/utils/platform.py @@ -4,11 +4,13 @@ def get_homedir(): - if sys.platform == 'win32': - homedir = os.environ['USERPROFILE'] - elif sys.platform == 'linux' or sys.platform == 'darwin': - homedir = os.environ['HOME'] + if sys.platform == "win32": + homedir = os.environ["USERPROFILE"] + elif sys.platform == "linux" or sys.platform == "darwin": + homedir = os.environ["HOME"] else: - raise QuafuError(f'unsupported platform:{sys.platform}. ' - f'You may raise a request issue on github.') + raise QuafuError( + f"unsupported platform:{sys.platform}. " + f"You may raise a request issue on github." + ) return homedir diff --git a/src/quafu/visualisation/circuitPlot.py b/src/quafu/visualisation/circuitPlot.py index 7b447ac..41b15c9 100644 --- a/src/quafu/visualisation/circuitPlot.py +++ b/src/quafu/visualisation/circuitPlot.py @@ -27,12 +27,12 @@ line_args = {} box_args = {} -DEEPCOLOR = '#0C161F' -BLUE = '#1f77b4' -ORANGE = '#ff7f0e' -GREEN = '#2ca02c' -GOLDEN = '#FFB240' -GARNET = '#C0392B' +DEEPCOLOR = "#0C161F" +BLUE = "#1f77b4" +ORANGE = "#ff7f0e" +GREEN = "#2ca02c" +GOLDEN = "#FFB240" +GARNET = "#C0392B" """ layers(zorder): @@ -45,18 +45,32 @@ 5: labels """ -su2_gate_names = ['x', 'y', 'z', 'id', 'w', - 'h', 't', 'tdg', 's', 'sdg', 'sx', 'sy', 'sw', - 'phase', - 'rx', 'ry', 'rz', - ] - -swap_gate_names = ['swap', 'iswap'] -r2_gate_names = ['rxx', 'ryy', 'rzz'] -c2_gate_names = ['cp', 'cs', 'ct', 'cx', 'cy', 'cz'] -c3_gate_names = ['fredkin', 'toffoli'] -mc_gate_names = ['mcx', 'mcy', 'mcz'] -operation_names = ['barrier', 'delay'] +su2_gate_names = [ + "x", + "y", + "z", + "id", + "w", + "h", + "t", + "tdg", + "s", + "sdg", + "sx", + "sy", + "sw", + "phase", + "rx", + "ry", + "rz", +] + +swap_gate_names = ["swap", "iswap"] +r2_gate_names = ["rxx", "ryy", "rzz"] +c2_gate_names = ["cp", "cs", "ct", "cx", "cy", "cz"] +c3_gate_names = ["fredkin", "toffoli"] +mc_gate_names = ["mcx", "mcy", "mcz"] +operation_names = ["barrier", "delay"] class CircuitPlotManager: @@ -66,9 +80,10 @@ class CircuitPlotManager: To be initialized when circuit.plot() is called. """ + # colors - _wire_color = '#FF0000' - _light_blue = '#3B82F6' + _wire_color = "#FF0000" + _light_blue = "#3B82F6" _ec = DEEPCOLOR _wire_lw = 1.5 @@ -77,7 +92,7 @@ class CircuitPlotManager: _a = 0.5 # box width and height, unit: ax _barrier_width = _a / 3 # barrier width - _stroke = pe.withStroke(linewidth=2, foreground='white') + _stroke = pe.withStroke(linewidth=2, foreground="white") def __init__(self, qc): """ @@ -124,21 +139,25 @@ def __init__(self, qc): self._proc_measure(self.depth - 1, q) # step2: initialize bit-label - self.q_label = {y: r'$|q_{%d}\rangle$' % i for i, y in self.used_qbit_y.items()} - self.c_label = {self.used_qbit_y[iq]: f'c_{ic}' for iq, ic in qc.measures.items()} + self.q_label = {y: r"$|q_{%d}\rangle$" % i for i, y in self.used_qbit_y.items()} + self.c_label = { + self.used_qbit_y[iq]: f"c_{ic}" for iq, ic in qc.measures.items() + } # step3: figure coordination self.xs = np.arange(-3 / 2, self.depth + 3 / 2) self.ys = np.arange(-2, self.used_qbit_num + 1 / 2) - def __call__(self, - title=None, - init_labels=None, - end_labels=None, - save_path: str = None, - show: bool = False, - *args, - **kwargs): + def __call__( + self, + title=None, + init_labels=None, + end_labels=None, + save_path: str = None, + show: bool = False, + *args, + **kwargs, + ): """ :param title :param init_labels: dict, {qbit: label} @@ -155,22 +174,27 @@ def __call__(self, # import random # plt.gca().xkcd(randomness=random.randint(0, 1000)) if title is not None: - title = Text((self.xs[0] + self.xs[-1]) / 2, -0.8, - title, - size=30, - ha='center', va='baseline') + title = Text( + (self.xs[0] + self.xs[-1]) / 2, + -0.8, + title, + size=30, + ha="center", + va="baseline", + ) self._text_list.append(title) # initialize a figure _size_x = self._a_inch * abs(self.xs[-1] - self.xs[0]) _size_y = self._a_inch * abs(self.ys[-1] - self.ys[0]) fig = plt.figure(figsize=(_size_x, _size_y)) # inch - ax = fig.add_axes([0, 0, 1, 1], - aspect=1, - xlim=[self.xs[0], self.xs[-1]], - ylim=[self.ys[0], self.ys[-1]], - ) - ax.axis('off') + ax = fig.add_axes( + [0, 0, 1, 1], + aspect=1, + xlim=[self.xs[0], self.xs[-1]], + ylim=[self.ys[0], self.ys[-1]], + ) + ax.axis("off") ax.invert_yaxis() self._circuit_wires() @@ -179,28 +203,29 @@ def __call__(self, self._render_circuit() if save_path is not None: - plt.savefig(save_path, dpi=300, bbox_inches='tight') + plt.savefig(save_path, dpi=300, bbox_inches="tight") if show: plt.show() def _process_ins(self, ins: Instruction, append: bool = True): name = ins.name - assert name in Instruction.ins_classes, 'If this should occur, please report a bug.' + assert ( + name in Instruction.ins_classes + ), "If this should occur, please report a bug." name = name.lower() _which = slice(np.min(ins.pos), np.max(ins.pos) + 1) depth = np.max(self.dorders[_which]) paras = ins.paras - - if name == 'barrier': + if name == "barrier": self._proc_barrier(depth, ins.pos) - elif name == 'measure': + elif name == "measure": self._proc_measure(depth, ins.pos) elif name in su2_gate_names: self._proc_su2(name, depth, ins.pos, paras) elif name in swap_gate_names: - self._proc_swap(depth, ins.pos, name == 'iswap') + self._proc_swap(depth, ins.pos, name == "iswap") elif name in r2_gate_names: # TODO: combine into one box self._proc_su2(name[-1], depth, ins.pos[0], paras) @@ -208,8 +233,10 @@ def _process_ins(self, ins: Instruction, append: bool = True): elif isinstance(ins, ControlledGate): self._proc_ctrl(depth, ins) else: - raise NotImplementedError(f'Gate {name} is not supported yet.\n' - f'If this should occur, please report a bug.') + raise NotImplementedError( + f"Gate {name} is not supported yet.\n" + f"If this should occur, please report a bug." + ) if append: self.dorders[_which] = depth + 1 @@ -227,129 +254,147 @@ def _circuit_wires(self): x1 = self.xs[-1] - 1 self._h_wire_points.append([[x0, y], [x1, y]]) - def _inits_label(self, labels: dict[int: str] = None): - """ qubit-labeling """ + def _inits_label(self, labels: dict[int:str] = None): + """qubit-labeling""" if labels is None: labels = self.q_label for i, label in labels.items(): - txt = Text(-2 / 3, i, - label, - size=18, - color=DEEPCOLOR, - ha='right', - va='center', - ) + txt = Text( + -2 / 3, + i, + label, + size=18, + color=DEEPCOLOR, + ha="right", + va="center", + ) self._text_list.append(txt) - def _measured_label(self, labels: dict[int: str] = None): - """ measured qubit-labeling """ + def _measured_label(self, labels: dict[int:str] = None): + """measured qubit-labeling""" if labels is None: labels = self.c_label for i, label in labels.items(): - label = r'$%s$' % label - txt = Text(self.xs[-1] - 3 / 4, i, - label, - size=18, - color=DEEPCOLOR, - ha='left', - va='center', - ) + label = r"$%s$" % label + txt = Text( + self.xs[-1] - 3 / 4, + i, + label, + size=18, + color=DEEPCOLOR, + ha="left", + va="center", + ) self._text_list.append(txt) def _gate_bbox(self, x, y, fc: str): - """ Single qubit gate box """ + """Single qubit gate box""" a = self._a from matplotlib.patches import FancyBboxPatch - bbox = FancyBboxPatch((-a / 2 + x, -a / 2 + y), a, a, # this warning belongs to matplotlib - boxstyle=f'round, pad={0.2 * a}', - edgecolor=DEEPCOLOR, - facecolor=fc, - ) + + bbox = FancyBboxPatch( + (-a / 2 + x, -a / 2 + y), + a, + a, # this warning belongs to matplotlib + boxstyle=f"round, pad={0.2 * a}", + edgecolor=DEEPCOLOR, + facecolor=fc, + ) self._closed_patches.append(bbox) def _gate_label(self, x, y, s): if not s: return None _dy = 0.05 - text = Text(x, y + _dy, - s, - size=24, - color=DEEPCOLOR, - ha='center', - va='center', - ) + text = Text( + x, + y + _dy, + s, + size=24, + color=DEEPCOLOR, + ha="center", + va="center", + ) text.set_path_effects([self._stroke]) self._text_list.append(text) def _para_label(self, x, y, para_txt): - """ label parameters """ + """label parameters""" if not para_txt: return None _dx = 0 - text = Text(x + _dx, y + 0.8 * self._a, - para_txt, - size=12, - color=DEEPCOLOR, - ha='center', - va='top', - ) + text = Text( + x + _dx, + y + 0.8 * self._a, + para_txt, + size=12, + color=DEEPCOLOR, + ha="center", + va="top", + ) self._text_list.append(text) def _measure_label(self, x, y): from matplotlib.patches import FancyArrow + a = self._a r = 1.1 * a d = 1.2 * a / 3.5 - arrow = FancyArrow(x=x, - y=y + d, - dx=0.15, - dy=-0.35, - width=0.04, - facecolor=DEEPCOLOR, - head_width=0.07, - head_length=0.15, - edgecolor='white') - arc = Arc((x, y + d), - width=r, - height=r, - lw=1, - theta1=180, - theta2=0, - fill=False, - zorder=4, - color=DEEPCOLOR, - capstyle='round', - ) - center_bkg = Circle((x, y + d), - radius=0.035, - color='white', - ) - center = Circle((x, y + d), - radius=0.025, - facecolor=DEEPCOLOR, - ) + arrow = FancyArrow( + x=x, + y=y + d, + dx=0.15, + dy=-0.35, + width=0.04, + facecolor=DEEPCOLOR, + head_width=0.07, + head_length=0.15, + edgecolor="white", + ) + arc = Arc( + (x, y + d), + width=r, + height=r, + lw=1, + theta1=180, + theta2=0, + fill=False, + zorder=4, + color=DEEPCOLOR, + capstyle="round", + ) + center_bkg = Circle( + (x, y + d), + radius=0.035, + color="white", + ) + center = Circle( + (x, y + d), + radius=0.025, + facecolor=DEEPCOLOR, + ) self._mea_arc_patches.append(arc) self._mea_point_patches += [center_bkg, arrow, center] ######################################################################### # region # # # # processing-functions: decompose ins into graphical elements # # # def _proc_su2(self, id_name, depth, pos, paras): - if id_name in ['x', 'y', 'z', 'h', 'id', 's', 't', 'p', 'u']: - fc = '#EE7057' + if id_name in ["x", "y", "z", "h", "id", "s", "t", "p", "u"]: + fc = "#EE7057" label = id_name.capitalize() - elif id_name in ['rx', 'ry', 'rz']: - fc = '#6366F1' + elif id_name in ["rx", "ry", "rz"]: + fc = "#6366F1" label = id_name.upper() else: - fc = '#8C9197' - label = '?' + fc = "#8C9197" + label = "?" - if id_name in ['rx', 'ry', 'rz', 'p']: + if id_name in ["rx", "ry", "rz", "p"]: # too long to display: r'$\theta=$' + f'{paras:.3f}' (TODO) - para_txt = f'({paras:.3f})' + para_txt = f"({paras:.3f})" else: para_txt = None @@ -376,17 +421,20 @@ def _proc_ctrl(self, depth, ins: ControlledGate, ctrl_type: bool = True): tar_name = ins.targ_name.lower()[-1] pos = ins.targs if isinstance(ins.targs, int) else ins.targs[0] x = self.used_qbit_y[pos] - if tar_name == 'x': + if tar_name == "x": self._not_points.append((depth, x)) else: self._proc_su2(tar_name, depth, pos, None) - elif name == 'cswap': + elif name == "cswap": self._swap_points += [[depth, self.used_qbit_y[p]] for p in ins.targs] - elif name == 'ccx': + elif name == "ccx": self._not_points.append((depth, self.used_qbit_y[ins.targs])) else: from quafu.elements.element_gates import ControlledU - assert isinstance(ins, ControlledU), f'unknown gate: {name}, {ins.__class__.__name__}' + + assert isinstance( + ins, ControlledU + ), f"unknown gate: {name}, {ins.__class__.__name__}" self._process_ins(ins, append=False) def _proc_swap(self, depth, pos, iswap: bool = False): @@ -404,8 +452,8 @@ def _proc_barrier(self, depth, pos: list): for p in pos: y = self.used_qbit_y[p] - y0 = (y - 1 / 2) - y1 = (y + 1 / 2) + y0 = y - 1 / 2 + y1 = y + 1 / 2 nodes = [[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]] self._barrier_points.append(nodes) @@ -421,6 +469,7 @@ def _proc_measure(self, depth, pos: int): # x0 = depth # x1 = self.depth - 1 / 2 # self._h_wire_points.append([[x0, y], [x1, y]]) + # endregion ######################################################################### @@ -428,45 +477,49 @@ def _proc_measure(self, depth, pos: int): # # # # # # # # # # # # # # rendering functions # # # # # # # # # # # # # ######################################################################### def _render_h_wires(self): - h_lines = LineCollection(self._h_wire_points, - zorder=0, - colors=self._wire_color, - alpha=0.8, - linewidths=2, - ) + h_lines = LineCollection( + self._h_wire_points, + zorder=0, + colors=self._wire_color, + alpha=0.8, + linewidths=2, + ) plt.gca().add_collection(h_lines) def _render_ctrl_wires(self): - v_lines = LineCollection(self._ctrl_wire_points, - zorder=0, - colors=self._light_blue, - alpha=0.8, - linewidths=4, - ) + v_lines = LineCollection( + self._ctrl_wire_points, + zorder=0, + colors=self._light_blue, + alpha=0.8, + linewidths=4, + ) plt.gca().add_collection(v_lines) def _render_closed_patch(self): - collection = PatchCollection(self._closed_patches, - match_original=True, - zorder=3, - ec=self._ec, - linewidths=0.5, - ) + collection = PatchCollection( + self._closed_patches, + match_original=True, + zorder=3, + ec=self._ec, + linewidths=0.5, + ) plt.gca().add_collection(collection) def _render_ctrl_nodes(self): circle_collection = [] r = self._a / 4 for x, y, ctrl in self._ctrl_points: - fc = '#3B82F6' if ctrl else 'white' + fc = "#3B82F6" if ctrl else "white" circle = Circle((x, y), radius=r, fc=fc) circle_collection.append(circle) - circles = PatchCollection(circle_collection, - match_original=True, - zorder=5, - ec=self._ec, - linewidths=2, - ) + circles = PatchCollection( + circle_collection, + match_original=True, + zorder=5, + ec=self._ec, + linewidths=2, + ) plt.gca().add_collection(circles) def _render_not_nodes(self): @@ -477,16 +530,16 @@ def _render_not_nodes(self): for x, y in self._not_points: points.append([[x, y - rp], [x, y + rp]]) points.append([[x - rp, y], [x + rp, y]]) - circle = Circle((x, y), radius=r, lw=1, - fc='#3B82F6') + circle = Circle((x, y), radius=r, lw=1, fc="#3B82F6") self._closed_patches.append(circle) - collection = LineCollection(points, - zorder=5, - colors='white', - linewidths=2, - capstyle='round', - ) + collection = LineCollection( + points, + zorder=5, + colors="white", + linewidths=2, + capstyle="round", + ) plt.gca().add_collection(collection) def _render_swap_nodes(self): @@ -495,49 +548,50 @@ def _render_swap_nodes(self): for x, y in self._swap_points: points.append([[x - r, y - r], [x + r, y + r]]) points.append([[x + r, y - r], [x - r, y + r]]) - collection = LineCollection(points, - zorder=5, - colors='#3B82F6', - linewidths=4, - capstyle='round', - ) + collection = LineCollection( + points, + zorder=5, + colors="#3B82F6", + linewidths=4, + capstyle="round", + ) plt.gca().add_collection(collection) # iswap-cirlces i_circles = [] for x, y in self._iswap_points: - circle = Circle((x, y), radius=2 ** (1 / 2) * r, lw=3, - ec='#3B82F6', fill=False) + circle = Circle( + (x, y), radius=2 ** (1 / 2) * r, lw=3, ec="#3B82F6", fill=False + ) i_circles.append(circle) - collection = PatchCollection(i_circles, - match_original=True, - zorder=5, - ) + collection = PatchCollection( + i_circles, + match_original=True, + zorder=5, + ) plt.gca().add_collection(collection) def _render_measure(self): - stroke = pe.withStroke(linewidth=4, foreground='white') - arcs = PatchCollection(self._mea_arc_patches, - match_original=True, - capstyle='round', - zorder=4) + stroke = pe.withStroke(linewidth=4, foreground="white") + arcs = PatchCollection( + self._mea_arc_patches, match_original=True, capstyle="round", zorder=4 + ) arcs.set_path_effects([stroke]) plt.gca().add_collection(arcs) - pointers = PatchCollection(self._mea_point_patches, # note the order - match_original=True, - zorder=5, - facecolors=DEEPCOLOR, - linewidths=2, - ) + pointers = PatchCollection( + self._mea_point_patches, # note the order + match_original=True, + zorder=5, + facecolors=DEEPCOLOR, + linewidths=2, + ) plt.gca().add_collection(pointers) def _render_barrier(self): - barrier = PolyCollection(self._barrier_points, - closed=True, - fc='lightgray', - hatch='///', - zorder=4) + barrier = PolyCollection( + self._barrier_points, closed=True, fc="lightgray", hatch="///", zorder=4 + ) plt.gca().add_collection(barrier) def _render_txt(self):