From 60ba527309572a150bec9dd6bef8638505d76e0d Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sun, 27 Aug 2023 20:24:23 +0800 Subject: [PATCH 1/4] Add TaskDatabase --- src/quafu/tasks/task_database.py | 167 +++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/quafu/tasks/task_database.py diff --git a/src/quafu/tasks/task_database.py b/src/quafu/tasks/task_database.py new file mode 100644 index 0000000..df68809 --- /dev/null +++ b/src/quafu/tasks/task_database.py @@ -0,0 +1,167 @@ +# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sqlite3 +from pathlib import Path + + +def print_task_info(task): + """ + Helper function to print task information. + """ + task_id, group_name, task_name, status, priority, send_time, finish_time = task + print(f"Task ID: {task_id}") + print(f"Group Name: {group_name}") + print(f"Task Name: {task_name}") + print(f"Status: {status}") + print(f"Priority: {priority}") + print(f"Send Time: {send_time}") + print(f"Finish Time: {finish_time}") + print("------------------------") + + +class QuafuTaskDatabase: + """ + A helper class use sqlite3 database to handle information of quafu tasks at local equipment. + When initialized and called, it will connect a database file named 'tasks.db' (create new + one if not exist) at the specified directory. The database will contain a table named 'tasks' + in which every 'task' has a unique task_id. Other attributes include group name, task name, + status, priority and send time. + + - Usage: + Use this class by 'with' statement. For example: + >>> with QuafuTaskDatabase(db_dir='./') as db: + ... db.insert_task(1, "Done", "Group 1", "Task1", priority=2) + ... print("Task list:") + ... for task_info in db.find_all_tasks(): + ... print_task_info(task_info) + This way ensures the database connection is closed and submission committed automatically. + """ + + def __init__(self, db_dir='./'): + self.database_name = "tasks.db" + self.database_dir = Path(db_dir) + self.conn = None + + def __enter__(self): + if not os.path.exists(self.database_dir): + os.makedirs(self.database_dir) + self.conn = sqlite3.connect(self.database_dir / self.database_name) + self._create_table() + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.conn: + self.conn.commit() + self.conn.close() + + def _create_table(self): + cursor = self.conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS tasks ( + task_id TEXT PRIMARY KEY, + group_name TEXT DEFAULT NULL, + task_name TEXT DEFAULT NULL, + status TEXT, + priority INTEGER, + send_time TIMESTAMP, + finish_time TIMESTAMP DEFAULT NULL + ) + ''') + cursor.close() + + # region data manipulation + def insert_task(self, + task_id, + status, + send_time: str, + priority=2, + group_name=None, + task_name=None, + finish_time: str = None + ): + cursor = self.conn.cursor() + cursor.execute( + "INSERT INTO tasks " + "(task_id, group_name, task_name, status, priority, send_time, finish_time) " + "VALUES " + "(?, ?, ?, ?, ?, ?, ?)", + (task_id, group_name, task_name, status, priority, send_time, finish_time)) + cursor.close() + + def delete_task(self, task_id): + cursor = self.conn.cursor() + cursor.execute("DELETE FROM tasks WHERE task_id=?", (task_id,)) + cursor.close() + print(f"Task {task_id} has been deleted from local database") + + def update_task_status(self, task_id, status): + cursor = self.conn.cursor() + cursor.execute("UPDATE tasks SET status=? WHERE task_id=?", (status, task_id)) + cursor.close() + # endregion + + # region fetch tasks + def find_all_tasks(self): + cursor = self.conn.cursor() + cursor.execute("SELECT * FROM tasks") + tasks = cursor.fetchall() + cursor.close() + return tasks + + def find_by_status(self, status): + cursor = self.conn.cursor() + cursor.execute("SELECT * FROM tasks WHERE status=?", (status,)) + tasks = cursor.fetchall() + cursor.close() + return tasks + + def find_by_priority(self, priority): + cursor = self.conn.cursor() + cursor.execute("SELECT * FROM tasks WHERE priority=?", (priority,)) + tasks = cursor.fetchall() + cursor.close() + return tasks + + def find_by_group(self, group_name): + cursor = self.conn.cursor() + if group_name is None: + cursor.execute("SELECT * FROM tasks WHERE group_name IS NULL") + else: + cursor.execute("SELECT * FROM tasks WHERE group_name=?", (group_name,)) + tasks = cursor.fetchall() + cursor.close() + return tasks + + def find_by_name(self, task_name): + cursor = self.conn.cursor() + if task_name is None: + cursor.execute("SELECT * FROM tasks WHERE task_name IS NULL") + else: + cursor.execute("SELECT * FROM tasks WHERE task_name=?", (task_name, )) + tasks = cursor.fetchall() + cursor.close() + return tasks + + def find_by_time(self, start_time, end_time): + """ + get tasks sent between start_time and end_time. + """ + cursor = self.conn.cursor() + cursor.execute("SELECT * FROM tasks WHERE send_time BETWEEN ? AND ?", (start_time, end_time)) + tasks = cursor.fetchall() + cursor.close() + return tasks + # endregion From 26fd311367238a2244675afc241c154cdf1031eb Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Fri, 1 Sep 2023 17:09:10 +0800 Subject: [PATCH 2/4] pull from master --- src/quafu/visualisation/circuitPlot.py | 94 ++++++++++++++------------ 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/quafu/visualisation/circuitPlot.py b/src/quafu/visualisation/circuitPlot.py index 74a2992..67c002d 100644 --- a/src/quafu/visualisation/circuitPlot.py +++ b/src/quafu/visualisation/circuitPlot.py @@ -20,7 +20,6 @@ from matplotlib.text import Text from quafu.elements.quantum_element import Instruction, ControlledGate -from typing import Dict # this line for developers only # from quafu.circuits.quantum_circuit import QuantumCircuit @@ -48,7 +47,7 @@ su2_gate_names = ['x', 'y', 'z', 'id', 'w', 'h', 't', 'tdg', 's', 'sdg', 'sx', 'sy', 'sw', - 'p', + 'phase', 'rx', 'ry', 'rz', ] @@ -108,15 +107,16 @@ def __init__(self, qc): self._text_list = [] + # step0: mapping y-coordinate of used-qubits + qubits_used = qc.used_qubits + self.used_qbit_num = len(qubits_used) + self.used_qbit_y = {iq: y for y, iq in enumerate(qubits_used)} + # step1: process gates/instructions self.dorders = np.zeros(qc.num, dtype=int) for gate in qc.gates: assert isinstance(gate, Instruction) self._process_ins(gate) - qubits_used = qc.used_qubits - self.used_qbit_num = len(qubits_used) - # TODO: drop off unused-qubits - # self.used_qbit_num = np.sum(qubits_used) self.depth = np.max(self.dorders) + 1 @@ -124,8 +124,8 @@ def __init__(self, qc): self._proc_measure(self.depth - 1, q) # step2: initialize bit-label - self.q_label = {i: r'$|q_{%d}\rangle$' % i for i in range(qc.num) if i in qubits_used} - self.c_label = {iq: r'c_{%d}' % ic for iq, ic in qc.measures.items() if iq in qubits_used} + 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]: r'c_{%d}' % ic for iq, ic in qc.measures.items()} # step3: figure coordination self.xs = np.arange(-3 / 2, self.depth + 3 / 2) @@ -161,7 +161,6 @@ def __call__(self, ha='center', va='baseline') self._text_list.append(title) - # TODO: adjust figure size according to title and other elements # 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]) @@ -222,23 +221,12 @@ def _circuit_wires(self): """ plot horizontal circuit wires """ - for y in range(self.used_qbit_num): + for pos, y in self.used_qbit_y.items(): x0 = self.xs[0] + 1 x1 = self.xs[-1] - 1 self._h_wire_points.append([[x0, y], [x1, y]]) - def _gate_bbox(self, x, y, fc: str): - """ 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, - ) - self._closed_patches.append(bbox) - - def _inits_label(self, labels: Dict[int, str] = None): + def _inits_label(self, labels: dict[int: str] = None): """ qubit-labeling """ if labels is None: labels = self.q_label @@ -269,7 +257,18 @@ def _measured_label(self, labels: dict[int: str] = None): ) self._text_list.append(txt) - def _gate_label(self, s, x, y): + def _gate_bbox(self, x, y, fc: str): + """ 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, + ) + self._closed_patches.append(bbox) + + def _gate_label(self, x, y, s): if not s: return None _dy = 0.05 @@ -283,7 +282,7 @@ def _gate_label(self, s, x, y): text.set_path_effects([self._stroke]) self._text_list.append(text) - def _para_label(self, para_txt, x, y): + def _para_label(self, x, y, para_txt): """ label parameters """ if not para_txt: return None @@ -335,8 +334,7 @@ def _measure_label(self, x, y): self._mea_point_patches += [center_bkg, arrow, center] ######################################################################### - # # # # processing-functions: decompose ins into graphical elements # # # - ######################################################################### + # 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' @@ -354,33 +352,37 @@ def _proc_su2(self, id_name, depth, pos, paras): else: para_txt = None - self._gate_label(label, depth, pos) - self._para_label(para_txt, depth, pos) - self._gate_bbox(depth, pos, fc) + x = depth + y = self.used_qbit_y[pos] + self._gate_label(x=x, y=y, s=label) + self._para_label(x=x, y=y, para_txt=para_txt) + self._gate_bbox(x=x, y=y, fc=fc) def _proc_ctrl(self, depth, ins: ControlledGate, ctrl_type: bool = True): # control part p0, p1 = np.max(ins.pos), np.min(ins.pos) - self._ctrl_wire_points.append([[depth, p1], [depth, p0]]) + x0, x1 = self.used_qbit_y[p0], self.used_qbit_y[p1] + self._ctrl_wire_points.append([[depth, x1], [depth, x0]]) ctrl_pos = np.array(ins.ctrls) for c in ctrl_pos: - self._ctrl_points.append((depth, c, ctrl_type)) + x = self.used_qbit_y[c] + self._ctrl_points.append((depth, x, ctrl_type)) # target part name = ins.name.lower() if ins.ct_nums == (1, 1, 2) or name in mc_gate_names: 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': - self._not_points.append((depth, pos)) + self._not_points.append((depth, x)) else: - para = ins.paras - self._proc_su2(tar_name, depth, pos, para) + self._proc_su2(tar_name, depth, pos, None) elif name == 'cswap': - self._swap_points += [[depth, p] for p in ins.targs] + self._swap_points += [[depth, self.used_qbit_y[p]] for p in ins.targs] elif name == 'ccx': - self._not_points.append((depth, ins.targs)) + 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__}' @@ -388,9 +390,10 @@ def _proc_ctrl(self, depth, ins: ControlledGate, ctrl_type: bool = True): def _proc_swap(self, depth, pos, iswap: bool = False): p1, p2 = pos - nodes = [[depth, p] for p in pos] + x1, x2 = self.used_qbit_y[p1], self.used_qbit_y[p2] + nodes = [[depth, x] for x in [x1, x2]] self._swap_points += nodes - self._ctrl_wire_points.append([[depth, p1], [depth, p2]]) + self._ctrl_wire_points.append([[depth, x1], [depth, x2]]) if iswap: self._iswap_points += nodes @@ -399,21 +402,26 @@ def _proc_barrier(self, depth, pos: list): x1 = depth + self._barrier_width for p in pos: - y0 = (p - 1 / 2) - y1 = (p + 1 / 2) + y = self.used_qbit_y[p] + y0 = (y - 1 / 2) + y1 = (y + 1 / 2) nodes = [[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]] self._barrier_points.append(nodes) - def _proc_measure(self, depth, pos): + def _proc_measure(self, depth, pos: int): fc = GOLDEN - self._gate_bbox(depth, pos, fc) - self._measure_label(depth, pos) + y = self.used_qbit_y[pos] + x = depth + self._gate_bbox(x, y, fc) + self._measure_label(x, y) # TODO: decide whether to draw double wire for measurement # y = pos + 0.02 # x0 = depth # x1 = self.depth - 1 / 2 # self._h_wire_points.append([[x0, y], [x1, y]]) + # endregion + ######################################################################### ######################################################################### # # # # # # # # # # # # # # rendering functions # # # # # # # # # # # # # From 2ba05ba9433c66b42fbc9566ea6fe7427679a0de Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Fri, 1 Sep 2023 17:12:22 +0800 Subject: [PATCH 3/4] some error corrected in master branch before --- src/quafu/visualisation/circuitPlot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quafu/visualisation/circuitPlot.py b/src/quafu/visualisation/circuitPlot.py index 67c002d..1c151a6 100644 --- a/src/quafu/visualisation/circuitPlot.py +++ b/src/quafu/visualisation/circuitPlot.py @@ -47,7 +47,7 @@ su2_gate_names = ['x', 'y', 'z', 'id', 'w', 'h', 't', 'tdg', 's', 'sdg', 'sx', 'sy', 'sw', - 'phase', + 'p', 'rx', 'ry', 'rz', ] From 047439d6c8c25d7a5e4d15f10c2e8673cc77359a Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Mon, 4 Sep 2023 17:32:45 +0800 Subject: [PATCH 4/4] set default ``send_time``=None to allow insert data from Task.send_history --- src/quafu/tasks/task_database.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quafu/tasks/task_database.py b/src/quafu/tasks/task_database.py index df68809..ffdfa43 100644 --- a/src/quafu/tasks/task_database.py +++ b/src/quafu/tasks/task_database.py @@ -43,7 +43,7 @@ class QuafuTaskDatabase: - Usage: Use this class by 'with' statement. For example: >>> with QuafuTaskDatabase(db_dir='./') as db: - ... db.insert_task(1, "Done", "Group 1", "Task1", priority=2) + ... db.insert_task(1, "Done", group_name="Group 1", task_name="Task1", priority=2) ... print("Task list:") ... for task_info in db.find_all_tasks(): ... print_task_info(task_info) @@ -86,7 +86,7 @@ def _create_table(self): def insert_task(self, task_id, status, - send_time: str, + send_time: str = None, priority=2, group_name=None, task_name=None,