From ab8a17763a4a5a35f5ea3571bc5e6e20346fd891 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 18 Nov 2023 21:13:41 +0800 Subject: [PATCH 1/5] fix some typo and path --- doc/source/apiref/apiref_0.3.5.rst | 4 ++- .../quafu_doc_0.3.5/quafu_doc_0_3_5.rst | 31 +++++++++---------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/doc/source/apiref/apiref_0.3.5.rst b/doc/source/apiref/apiref_0.3.5.rst index 8aed751..e9b1bfb 100644 --- a/doc/source/apiref/apiref_0.3.5.rst +++ b/doc/source/apiref/apiref_0.3.5.rst @@ -5,6 +5,8 @@ API Reference This page is still under refinement, the contents you see may be incomplete at present. +.. _quantum_circuit: + Quantum Circuit ------------------ @@ -17,7 +19,7 @@ Quantum Elements .. hint:: hello -.. autoclass:: quafu.elements.quantum_element.Instruction +.. autoclass:: quafu.elements.Instruction :members: diff --git a/doc/source/user_guide/quafu_doc_0.3.5/quafu_doc_0_3_5.rst b/doc/source/user_guide/quafu_doc_0.3.5/quafu_doc_0_3_5.rst index 5064c83..bb393ba 100644 --- a/doc/source/user_guide/quafu_doc_0.3.5/quafu_doc_0_3_5.rst +++ b/doc/source/user_guide/quafu_doc_0.3.5/quafu_doc_0_3_5.rst @@ -25,7 +25,7 @@ Set up your Quafu account If you haven’t have an account, you may register on the `Quafu `__ website at first. Then you will -find your apitoken ````\ on the ``Dashboard`` page, +find your apitoken ```` on the ``Dashboard`` page, which is required when you send tasks to ScQ-chips. By executing the following codes your token will be saved to your local @@ -98,12 +98,11 @@ circuit. gate = qeg.XGate(pos=0) qc.add_gate(gate) -This is actually what ’\ ``.name(args)`` functions do. You would find +This is actually what ``.name(args)`` functions do. You would find the second style convenient when build a new circuit from existing one. -For quantum gates Quafu supports, please check the API reference for -```QuantumCircuit`` `__ or use -python-buitin ``dir()`` method. +For quantum gates Quafu supports, please check the API reference for :ref:`quantum_circuit` +or use python-buitin ``dir()`` method. .. code:: python @@ -136,8 +135,7 @@ Visualize From ``version=0.3.2``, ``PyQuafu`` provides two similiar ways to visualize quantum circuits. You can draw the circuit using the -```draw_circuit`` `__ -method and use ``width`` parameter to adjust the length of the circuit. +``draw_circuit`` method and use ``width`` parameter to adjust the length of the circuit. .. code:: python @@ -224,7 +222,7 @@ First, initialize a Task object task = Task() You can configure your task properties using the -```config`` `__ method. Here we +``config`` method. Here we choose the backend (``backend``) as ``ScQ-P18``, the single shots number (``shots``) as 2000 and compile the circuit on the backend (``compile``). @@ -286,8 +284,7 @@ provide simple circuit similator If you don’t want to plot the results for basis with zero probabilities, set the parameter ``full`` in method -```plot_probabilities`` `__ -to False. Note that this parameter is only valid for results returned by +``plot_probabilities`` to False. Note that this parameter is only valid for results returned by the simulator. A Subtle detail @@ -347,11 +344,11 @@ Measure observables Quafu provides measuring observables with an executed quantum circuit. You can input Pauli operators that need to measure expectation values to -the ```submit`` `__ method. For +the ``submit`` `__ method. For example, you can input [[“XYX”, [0, 1, 2]], [“Z”, [1]]] to calculate the expectation of operators :math:`\sigma^x_0\sigma^y_1\sigma^x_2` and :math:`\sigma^z_1`. The -```submit`` `__ method will +``submit`` `__ method will minimize the executing times of the circuit with different measurement basis that can calculate all expectations of input operators. @@ -387,7 +384,7 @@ First, we initialize a circuit with three Hadamard gate Next, we set operators that need to be measured to calculate the energy expectation, and submit the circuit using -```submit`` `__ method +``submit`` method .. code:: python @@ -438,7 +435,7 @@ Submit task asynchronously In the above examples, we chose opening python kernal and waiting for the result. You may also set the ``wait=False`` in -```send`` `__ function to submit +``send`` function to submit the task asynchronously. Here we use another example that measures the qubit decoherence time :math:`T_1` to demonstrate the usage. @@ -461,13 +458,13 @@ Prepare parameters of a group of tasks and send the task asynchronously. res = task.send(q, wait=False, name=name, group="Q3_T1") Here the ``delay`` options will idle the target qubit ``2`` for a -duration ``t`` in the time unit ``us``\ (microsecond) and do nothing. In +duration ``t`` in the time unit ``us`` (microsecond) and do nothing. In the send function, we set ``wait`` to false to execute the task asynchronously, give each task a name by duration time and set all tasks -to a group named “Q3_T1”. +to a group named "Q3_T1". Now we can try to retrieve the group of tasks using the -```retrieve_group`` `__ +``retrieve_group`` method. .. code:: python From 3ddd6e74e5ec38922bb168682004123465d2d18c Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 18 Nov 2023 21:15:22 +0800 Subject: [PATCH 2/5] write named_pos explicitly --- src/quafu/elements/instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quafu/elements/instruction.py b/src/quafu/elements/instruction.py index fba9d68..0743256 100644 --- a/src/quafu/elements/instruction.py +++ b/src/quafu/elements/instruction.py @@ -87,7 +87,7 @@ def __init__(self, pos): @property def named_pos(self): - return self.named_pos + return {'pos': self.pos} @property def named_paras(self): From fb3c7a5e16f4898303fd88e010bb1835940fa310 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sat, 18 Nov 2023 22:14:59 +0800 Subject: [PATCH 3/5] slightly reformat --- src/quafu/elements/matrices/mat_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quafu/elements/matrices/mat_utils.py b/src/quafu/elements/matrices/mat_utils.py index 1b024d4..8d55ed2 100644 --- a/src/quafu/elements/matrices/mat_utils.py +++ b/src/quafu/elements/matrices/mat_utils.py @@ -17,6 +17,7 @@ def reorder_matrix(matrix: np.ndarray, pos: List): tensorm = matrix.reshape([2] * 2 * qnum) return np.transpose(tensorm, inds).reshape([dim, dim]) + def split_matrix(matrix: ndarray): """ Evenly split a matrix into 4 sub-matrices. From 00c7555da40d93c603d07e652557c6d749062945 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Thu, 30 Nov 2023 15:18:54 +0800 Subject: [PATCH 4/5] feat: add oracle module (from master) --- src/quafu/elements/oracle.py | 119 +++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/quafu/elements/oracle.py diff --git a/src/quafu/elements/oracle.py b/src/quafu/elements/oracle.py new file mode 100644 index 0000000..2ce6e47 --- /dev/null +++ b/src/quafu/elements/oracle.py @@ -0,0 +1,119 @@ +from abc import ABCMeta +from quafu.elements import QuantumGate, Instruction +from typing import Dict, Iterable, List +import copy + + +class OracleGateMeta(ABCMeta): + """ + Metaclass to create OracleGate CLASS which is its instance. + """ + + def __init__(cls, name, bases, attrs): + for attr_name in ['cls_name', 'gate_structure', 'qubit_num']: + assert attr_name in attrs, f"OracleGateMeta: {attr_name} not found in {attrs}." + + # TODO: check if instructions inside gate_structure are valid + + super().__init__(name, bases, attrs) + cls.name = attrs.__getitem__('cls_name') + cls.gate_structure = attrs.__getitem__('gate_structure') + cls.qubit_num = attrs.__getitem__('qubit_num') + + +class OracleGate(QuantumGate): # TODO: Can it be related to OracleGateMeta explicitly? + """ + OracleGate is a gate that can be customized by users. + """ + name = None + gate_structure = [] + qubit_num = 0 + + _named_pos = {} + insides = [] + + def __init__(self, pos: List, paras=None, label: str = None): + """ + Args: + pos: position of the gate + paras: parameters of the gate # TODO: how to set paras? + label: label when draw or plot + """ + if not self.qubit_num == len(pos): + raise ValueError(f"OracleGate: qubit number {self.qubit_num} does not match pos length {len(pos)}.") + super().__init__(pos=pos, paras=paras) + + self.__instantiate_gates__() + self.label = label if label is not None else self.name + + @property + def matrix(self): + # TODO: this should be finished according to usage in simulation + # to avoid store very large matrix + raise NotImplemented + + @property + def named_pos(self) -> Dict: + return {'pos': self.pos} + + @property + def named_paras(self) -> Dict: + # TODO: how to manage paras and the names? + return self._named_pos + + def to_qasm(self): + # TODO: this is similar to QuantumCircuit.to_qasm + raise NotImplemented + + def __instantiate_gates__(self) -> None: + """ + Instantiate the gate structure through coping ins and bit mapping. + """ + bit_mapping = {i: p for i, p in enumerate(self.pos)} + + def map_pos(pos): + if isinstance(pos, int): + return bit_mapping[pos] + elif isinstance(pos, Iterable): + return [bit_mapping[p] for p in pos] + else: + raise ValueError + + for gate in self.gate_structure: + gate_ = copy.deepcopy(gate) + for key, val in gate.named_pos.items(): + setattr(gate_, key, map_pos(val)) + setattr(gate_, 'pos', map_pos(gate.pos)) + self.insides.append(gate_) + + +def customize_gate(cls_name: str, + gate_structure: List[Instruction], + qubit_num: int, + ): + """ + Helper function to create customized gate class + + Args: + cls_name: name of the gate class + gate_structure: a list of instruction INSTANCES + qubit_num: number of qubits of the gate (TODO: extract from gate_structure?) + + Returns: + customized gate class + + Raises: + ValueError: if gate class already exists + """ + if cls_name in QuantumGate.gate_classes: + raise ValueError(f"Gate class {cls_name} already exists.") + + attrs = {'cls_name': cls_name, + 'gate_structure': gate_structure, # TODO: translate + 'qubit_num': qubit_num, + } + + customized_cls = OracleGateMeta(cls_name, (OracleGate,), attrs) + assert issubclass(customized_cls, OracleGate) + QuantumGate.register_gate(customized_cls, cls_name) + return customized_cls From f8242f4a9d7cd95a3d6bd5fce8723dd0e5b38159 Mon Sep 17 00:00:00 2001 From: chensgit169 Date: Sun, 17 Dec 2023 16:59:49 +0800 Subject: [PATCH 5/5] feat: visualization of bloch sphere (cherry picked from commit d1442e1a1564be1c73e6866bda394783addd37a0) --- src/quafu/visualisation/bloch_sphere.py | 95 +++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/quafu/visualisation/bloch_sphere.py diff --git a/src/quafu/visualisation/bloch_sphere.py b/src/quafu/visualisation/bloch_sphere.py new file mode 100644 index 0000000..c72676f --- /dev/null +++ b/src/quafu/visualisation/bloch_sphere.py @@ -0,0 +1,95 @@ +import matplotlib.pyplot as plt +import numpy as np + +""" +Plotting state of single qubit on the Bloch sphere. + +TODO: +1. Plot by density matrix, say, from single-qubit sub-system. +2. Plot geometrical representation of quantum operations. +3. Plot a chain of qubits. +""" + + +def angles_to_xyz(thetas, phis): + """Transform angles to cartesian coordinates.""" + xs = np.sin(thetas) * np.cos(phis) + ys = np.sin(thetas) * np.sin(phis) + zs = np.cos(thetas) + return xs, ys, zs + + +def xyz_to_angles(xs, ys, zs): + """Transform cartesian coordinates to angles.""" + phis = np.arctan2(ys, xs) + thetas = np.arccos(zs) + return thetas, phis + + +def hex_to_rgb(hex_color): + """Transform a hex color code to RGB (normalized float).""" + hex_color = hex_color.lstrip('#') + if len(hex_color) != 6: + raise ValueError("Invalid hex color code") + + r = int(hex_color[0:2], 16) / 255 + g = int(hex_color[2:4], 16) / 255 + b = int(hex_color[4:6], 16) / 255 + return r, g, b + + +def plot_bloch_vector(v_x, v_y, v_z, title=""): + """ + Plot the Bloch vector on the Bloch sphere. + + Args: + v_x (float): x coordinate of the Bloch vector. + v_y (float): y coordinate of the Bloch vector. + v_z (float): z coordinate of the Bloch vector. + title (str): title of the plot. + + Returns: + ax: matplotlib axes of the Bloch sphere plot. + """ + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # surface of Bloch sphere + theta = np.linspace(0, np.pi, 21) + phi = np.linspace(0, 2 * np.pi, 21) + theta, phi = np.meshgrid(theta, phi) + x, y, z = angles_to_xyz(theta, phi) + + surf = ax.plot_surface(x, y, z, color='white', alpha=0.2) + edge_color = hex_to_rgb('#000000') # #ff7f0e + edge_alpha = 0.05 + surf.set_edgecolor((edge_color[0], edge_color[1], edge_color[2], edge_alpha)) + + # coordinate axes inside the sphere span + span = np.linspace(-1.0, 1.0, 2) + ax.plot(span, 0 * span, zs=0, zdir="z", label="X", lw=1, color="black", alpha=0.5) + ax.plot(0 * span, span, zs=0, zdir="z", label="Y", lw=1, color="black", alpha=0.5) + ax.plot(0 * span, span, zs=0, zdir="y", label="Z", lw=1, color="black", alpha=0.5) + + # coordinate values + ax.text(1.4, 0, 0, 'x', color='black') + ax.text(0, 1.2, 0, 'y', color='black') + ax.text(0, 0, 1.2, 'z', color='black') + + # Bloch vector + ax.quiver(0, 0, 0, v_x, v_y, v_z, color='r') + v_theta, v_phi = xyz_to_angles(v_x, v_y, v_z) + + # coordinates value text + ax.text(0, 0, 1.6, 'Bloch vector: ($\\theta=${:.2f}, $\\varphi$={:.2f})'.format(v_theta, v_phi), fontsize=8, color='red') + # ax.text(0, 0, 1.6, 'Bloch vector: ({:.2f}, {:.2f}, {:.2f})'.format(v_x, v_y, v_z), fontsize=8, color='red') + + # Set the range of the axes + ax.set_box_aspect([1, 1, 1]) + ax.set_xlim(-1, 1) + ax.set_ylim(-1, 1) + ax.set_zlim(-1, 1) + ax.view_init(32, 32) + ax.set_axis_off() + ax.set_title(title) + return ax