Repository: mit-han-lab/torchquantum (language: python)

Issue:
add supports for more basic operations and layers
In order to enrich the supported operations of tq, we need your help on adding more functionalities. 

Please firstly identity the gates that have not been supported by torchquantum, you can find them by having a look at other frameworks such as Qiskit, Cirq etc. You can identity the gates that not supported by looking at qiskit.circuit.library.
Such as the SdG gate:
[SdgGate](https://qiskit.org/documentation/stubs/qiskit.circuit.library.SdgGate.html#qiskit.circuit.library.SdgGate)

For the simplest non-parameterized gates, you need firstly add their unitary matrix to tq.functional file: https://github.com/mit-han-lab/torchquantum/blob/8d06a998a6d35374907c5e75a347d187e837b8cc/torchquantum/functional.py#L1104 and also add their functions. Then you can add a class to the tq.operators files and finally add test function to test/test_op.py. You may create new test files.

You may also implement other template layers according to qiskit.circuit.library. https://qiskit.org/documentation/apidoc/circuit_library.html

Please let us know if you have any questions or need any instructions!


Adding parameter shift gradient as a fucntion
#133 i made a function to calculate the gradient of a given model, it returns a tensor of the gradients using the parameter shift rule as a part of the unitaryhack, added  to `utils.py`   
add supports for more basic operations and layers
In order to enrich the supported operations of tq, we need your help on adding more functionalities. 

Please firstly identity the gates that have not been supported by torchquantum, you can find them by having a look at other frameworks such as Qiskit, Cirq etc. You can identity the gates that not supported by looking at qiskit.circuit.library.
Such as the SdG gate:
[SdgGate](https://qiskit.org/documentation/stubs/qiskit.circuit.library.SdgGate.html#qiskit.circuit.library.SdgGate)

For the simplest non-parameterized gates, you need firstly add their unitary matrix to tq.functional file: https://github.com/mit-han-lab/torchquantum/blob/8d06a998a6d35374907c5e75a347d187e837b8cc/torchquantum/functional.py#L1104 and also add their functions. Then you can add a class to the tq.operators files and finally add test function to test/test_op.py. You may create new test files.

You may also implement other template layers according to qiskit.circuit.library. https://qiskit.org/documentation/apidoc/circuit_library.html

Please let us know if you have any questions or need any instructions!




Discussion / hints:

Fix patch:
diff --git a/examples/grover/README.md b/examples/grover/README.md
new file mode 100644
index 00000000..91437baa
--- /dev/null
+++ b/examples/grover/README.md
@@ -0,0 +1,8 @@
+# Grover's Search Algorithm
+
+Grover's Search Algorithm [1] is an algorithm which can speed up an unstructured search problem quadratically using amplitude amplification. A detailed walkthrough can be found [here](https://quantum-computing.ibm.com/composer/docs/iqx/guide/grovers-algorithm). The file `grover_example_sudoku.py` provides an example of how to use the algorithm to solve a sudoku puzzle of size 2x2.
+
+
+## References
+
+1. Grover, Lov K.. “A fast quantum mechanical algorithm for database search.” Symposium on the Theory of Computing (1996).
\ No newline at end of file
diff --git a/examples/grover/grover_example_sudoku.py b/examples/grover/grover_example_sudoku.py
new file mode 100644
index 00000000..25761594
--- /dev/null
+++ b/examples/grover/grover_example_sudoku.py
@@ -0,0 +1,93 @@
+""" 
+This example is based on Qiskt's Texbook: https://learn.qiskit.org/course/ch-algorithms/grovers-algorithm#sudoku
+
+We will now tackle a 2x2 binary sudoku problem using Grover's algorithm,  where we don't necessarily possess 
+prior knowledge of the solution. The problem adheres to two simple rules:
+
+1. No column can have the same value repeated.
+2. No row can have the same value repeated.
+
+We will use the following 4 variables:
+
+---------
+| a | b |
+---------
+| c | d |
+---------
+
+Please keep in mind that while utilizing Grover's algorithm to solve this particular problem may not be practical 
+(as the solution can likely be determined mentally), the intention of this example is to showcase the process of 
+transforming classical decision problems into oracles suitable for Grover's algorithm.
+
+We need to check for four conditions:
+
+1. a != b
+2. c != d
+3. a != c
+4. b != d
+"""
+
+import torchquantum as tq
+from torchquantum.algorithms import Grover
+
+
+# To simplify the process, we can compile this set of comparisons into a list of clauses for convenience.
+clauses = [ [0, 1], [0, 2], [1, 3], [2, 3] ]
+
+# This circuit checks if input0 is equal to input1 and stores the output in output. 
+# The output of each comparison is stored in a new bit.
+def XOR(input0, input1, output):
+    op1 = {'name': 'cnot', 'wires': [input0, output]}
+    op2 = {'name': 'cnot', 'wires': [input1, output]}
+    return [op1, op2]
+    
+# To verify each clause, we repeat the above circuit for every pairing in the `clauses`. 
+ops = []
+clause_qubits = [4, 5, 6, 7]
+for i, clause in enumerate(clauses):
+	ops += XOR(clause[0], clause[1], clause_qubits[i])
+
+# To determine if the assignments of a, b, c, d are a solution to the sudoku, we examine the final state 
+# of the `clause_qubits`. Only when all of these qubits are 1, it indicates that the clauses are satisfied. 
+# To achieve this, we incorporate a multi-controlled Toffoli gate in our checking circuit. This gate 
+# ensures that a single output bit will be set to 1 if and only if all the clauses are satisfied, 
+# allowing us to easily determine if our assignment is a solution.
+ops += [{'name': 'multicnot', 'n_wires': 5, 'wires': [4,5,6,7,8]}]
+
+# In order to transform our checking circuit into a Grover oracle, it is crucial to ensure that the `clause_qubits` 
+# are always returned to their initial state after the computation. This guarantees that `clause_qubits` are all 
+# set to 0 once our circuit has finished running. To achieve this, we include a step called "uncomputation" 
+# where we repeat the segment of the circuit that computes the clauses. This uncomputation step ensures the 
+# desired state restoration, enabling us to effectively use the circuit as a Grover oracle.
+for qubit, clause in enumerate(clauses):
+	ops += XOR(clause[0], clause[1], qubit + 4)
+
+# Full Algorithm
+# We can combine all the components we have discussed so far
+
+qmodule = tq.QuantumModule.from_op_history(ops)
+iterations = 2
+qdev = tq.QuantumDevice(n_wires=9, device="cpu")
+
+# Initialize output qubit (last qubit) in state |->
+qdev.x(wires=8)
+qdev.h(wires=8)
+
+# Perform Grover's Search
+grover = Grover(qmodule, iterations, 4)
+result = grover.execute(qdev)
+bitstring = result.bitstring[0]
+
+# Extract the top two most likely solutions
+res = {k: v for k, v in sorted(bitstring.items(), key=lambda item: item[1], reverse=True)}
+
+# Print the top two most likely solutions
+top_keys = list(res.keys())[:2]
+print("Top two most likely solutions:")
+for key in top_keys:
+	print("Solution: ", key)
+	print("a = ", key[0])
+	print("b = ", key[1])
+	print("c = ", key[2])
+	print("d = ", key[3])
+	print("")
\ No newline at end of file
diff --git a/examples/index.rst b/examples/index.rst
index 2f5fc51b..e36da434 100644
--- a/examples/index.rst
+++ b/examples/index.rst
@@ -8,3 +8,4 @@ TorchQuantum Examples
    param_shift_onchip_training/param_shift_onchip_training.ipynb
    quantum_kernel_method/quantum_kernel_method.ipynb
    quanvolution/quanvolution.ipynb
+   superdense_coding/superdense_coding_torchquantum.ipynb
\ No newline at end of file
diff --git a/examples/superdense_coding/README.md b/examples/superdense_coding/README.md
new file mode 100644
index 00000000..bfb4e7ee
--- /dev/null
+++ b/examples/superdense_coding/README.md
@@ -0,0 +1,13 @@
+# Superdense Coding
+
+Superdense coding is a quantum communication protocol that allows the transmission of two classical bits of information using only one qubit`[1]`. It takes advantage of quantum entanglement and the ability to manipulate qubits in superposition. Charles H. Bennett and Stephen Wiesner proposed this technique in 1970(though it was not published until 1992`[2]`) and it was experimentally realised in 1996 by Klaus Mattle, Harald Weinfurter, Paul G. Kwiat, and Anton Zeilinger utilising entangled photon pairs.
+
+## Author
+
+[Soham Bopardikar](https://github.com/bopardikarsoham)
+
+## References
+
+[1] Bennett, C.H., Brassard, G., Crépeau, C., Jozsa, R., Peres, A. and Wootters, W.K., 1993. Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels. Physical review letters, 70(13), p.1895.
+
+[2] Bennett, C.H. and Wiesner, S.J., 1992. Communication via one-and two-particle operators on Einstein-Podolsky-Rosen states. Physical review letters, 69(20), p.2881.
\ No newline at end of file
diff --git a/examples/superdense_coding/superdense_coding_torchquantum.ipynb b/examples/superdense_coding/superdense_coding_torchquantum.ipynb
new file mode 100644
index 00000000..3f53e91c
--- /dev/null
+++ b/examples/superdense_coding/superdense_coding_torchquantum.ipynb
@@ -0,0 +1,538 @@
+{
+  "cells": [
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "dd5XGIjBjd_K"
+      },
+      "source": [
+        "**Author:** Soham Bopardikar  \n",
+        "**Advisor:** Hanrui Wang"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "ODpUdXvMrYuD"
+      },
+      "source": [
+        "# Superdense Coding\n",
+        "Superdense coding is a quantum communication protocol that allows the transmission of two classical bits of information using only one qubit`[1]`. It takes advantage of quantum entanglement and the ability to manipulate qubits in superposition. Charles H. Bennett and Stephen Wiesner proposed this technique in 1970(though it was not published until 1992 `[2]`) and it was experimentally realised in 1996 by Klaus Mattle, Harald Weinfurter, Paul G. Kwiat, and Anton Zeilinger utilising entangled photon pairs.\n",
+        "\n",
+        "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfoAAACKCAIAAADNIh9rAAAf20lEQVR4nO3deVQTSRoA8AJDOAXkCMKABFCUQwUUEFCW8YwK6qgrjj7BY/QpyqocjrteuLs6jjMjOqIzqyujqG/Q8Vp1vQYFOVVUVFgFhuWQW64kBCEJSe8fvZuX5QiBdLpzfL8/fKa6u+rrovkSqqsrOhiGIaD1oqOjjx8/TnUUBNCYE6EKdKAG04F0DxBCOjoaciVozIlQBTpQg+lSHQAAAAAyQLoHAACtAOkeAAC0AqR7AADQCjSqAwAAaA49Pb3u7m7SmqPRaEKhkLTm1B2kewAAYbq7u8mc2KOjo0NaWxoA0r1SPo9o24cOAvtQq7oOrj1AJkj3Svk8om0fOgjsQ63qOrj2AJngVi0AQKW1tbXduHGD6ig0AaR7gBBC48ePpzoEYmjMiVBFBTswISEhIyOD6ig0ATwwrZSnxrXtSXQCz1eruk7zrj3CWy8vL/f39y8qKrKxsSGhOc0GY/d9a29v//DhA51O19XVxTBMJBJhGMZkMjs6Ourr6/X19fERUpFIJBKJnJ2dqY5XRXE4nJqamv622tvbm5mZkRmPWoBrT1pcXFx8fHyfuR4MlhLTPYZhNTU1NjY2dDodISQQCPD/qIWGhoZLly49fvw4LS1t+PDhUVFRrq6ua9eubW5uTk1Nzc3NvXv3roGBQXR0tLOz88aNG6mOV0Vdv359zZo1+vr6M2fOHDNmjI2NDZ6qkpKSampqrly5smTJEqpjVDlw7Unk5eU9f/784sWLVAeiKTDlyM7ODg0N/emnn/bt2/fw4cNNmzalpaUNqoaurq5p06Zt2bJFSRFKyOiE58+fI4RCQ0N7lBcXFyOEQkJChlCnRurvfI8fP+7o6FhUVCRdeP/+fYTQ9OnTB1WVRtK8a4/A1sVicWBg4MWLF8lpThso5dP9tWvXdu7cmZ2dzWAwEELh4eHXr1//9ttvB1UJh8N5+vRpW1ubMiKUk4GBgeRfecpBDzweb//+/R4eHpKSjx8/bty40cDA4Mcff6QwMNUH115qampnZ+fy5cupDkRzEJ/uGxoa1qxZc+LECTzXI4SMjIz8/PyMjIwQQs+fP793756xsbGHh8fs2bNl1MNgMCoqKoYPH054hOqrpaWltbV19OjR6jK3msfjsVgs6ZKEhISKiooDBw6MGTOGqqgGS+26XQMIBII9e/acOnVKVxdmDxKG+K7cs2ePWCz+/e9/LynJysr63e9+hxB6+fLl119/vXPnzu3bt+fk5Hz11Veyq7Kzs4N0j3v79m1AQICVlZWrq6u1tfXZs2epjkguISEh0jfZXr16lZiY6OnpGR8fT2FU8lPTbtcAx44dGz9+/PTp06kORLMQPjxkZ2c3f/58ycvq6mqE0IMHDzAM8/X1lYzgd3V1mZmZcTgcwgMYLBmdUFRUhBBaunRpj/LKykqEEIvFGkKdQ1BfX29hYdHjB/fTTz8R2ISC5DlfkUjk6+uro6OTm5urYFXkIKHbVf/aGyxCWm9tbWUwGG/fviWnOe1B8GBOY2NjXV3dli1bJCWPHj2i0WiBgYFsNvv58+cuLi54ub6+vpmZ2YMHD5YuXUpsDIR79uzZqlWrpEt4PB6ZARw4cKC1tbVHYVxcXGRkpBoNLxw/fjw/Pz8qKiogIIDqWOSiCt1O+bWnbGKxmMPhcLlcOp1uYGBgbm6uo6OTkJAQHh7u5uZGdXSahuB0jw+0SQ/LpqWl+fr6GhsbP3v2DMMwfAQfZ2Rk1NDQIKO2trY2IyMjfX39Adt1cnLCP/Uog5+f3/nz56VLqqqqBnyqW9kZoaWlhcBhzWHDholEIqJq6626unr37t12dnYDjuAhxbpO2SdCbLcPiPxrT9kdKBKJnjx58vjx49zc3JKSkvfv3xsbG5uamgqFws7Ozs7OzlGjRlVVVe3Zs6eoqMjT03PACin5xEOj0chc55mQRplMJsHp3tra2tPTk81m4y/T09NTU1NjY2MRQnw+H/3/zwZ/Y++vqpaWFkdHRzc3t/z8/AHbraioGHLMSrpcMOIe9mOxWPjkxR6am5stLS0JaQJ/omfIhw/Yh5s3b+bxeOfOnTM1NZUU3r59OzQ0tPfOikSi4IlII6HbVfDaU96V8Ntvv/3444+pqak2NjaffvrpunXrPDw8nJyc9PT0JPt8/PhxwYIFXl5e1dXVYWFhhoaGK1eu3LBhg7W1dX/VEviLpvGIn5mTnJwcGxtraWlZVlYmFouFQmFISAhCCP89l3534nA4Mu7EGhkZOTs7wx90CKHQ0NDeeScwMJCopIOU/Dtz5cqVW7duLViwYPHixZJCgUCQkZHRZ7pXBIEnQkK3qyBlXAnv3r3bu3dvZmbm+vXr09PTXV1d+9vz9evXpaWlxcXFRkZGGIY9e/YsOTl57Nixn3/++b59+yST/cAQKeOGQFdXV2FhIZ/PT0lJodFo7e3tGIbx+XwDA4PXr19LdtPX18/KylJGAIMioxNU5HaZSCTqMWnVysrq3bt3BDahYMAyDmez2ba2tiYmJtXV1dLlhYWFf/7zn8mMZLCo7Xaqrj1iD+/o6IiPj2cwGEeOHOHxeLKP7e+5qg8fPsTExFhbWx87dkwsFhMYrbZRyiikvr6+p6cnnU7PyMiYNGmSiYkJQohOp4eHh798+RLfp6Kigslk+vn5KSMADaOrq3v37t2kpKR58+YhhLZt2/avf/1r3LhxVMcll507d9bX1x88eNDe3l66PCUlZcSIEVRFJQ+17nZVUFRU5OfnV1dXV1hYuH37dmNjY9n79/dclbW19XfffZeVlfXLL7/MmTOnvr5eaSFrOuW9k4hEIkdHx7i4OElJW1vbnDlzrl+/np2dvXjxYvzmLeVkdEJOTg5CaPbs2T3KX79+jRAKCAgYQp0KUlLNClbb3+HZ2dk6Ojp+fn74Ol84sVicmJiop6d3/vx50iJREPnVUnXtEXX4zZs3GQxGSkqKnAfy+XwXF5eHDx/K2Ke7uzshIcHBwUEySKDUDKZ5lNVZe/fuDQkJ0dPT8/DwiImJkZR3d3fn5+dnZmbiIzyqoM8r5sWLFxEREb6+vmZmZlZWVsuXL9+zZw+GYUVFRZGRkVOmTDEzMxsxYsSyZcvi4+PlrFN50VJebX+Hz5o1CyE0YcKE0NDQ0NDQ+fPnBwcHS2673b59m7RIFERmtdRee4QcfuHCBVtb2/z8fPkPPHz48KJFi+TZ89KlSwwGA1+ICdL9oChrteju7m4ajdb7/ypIvdYcV1LNClarOuvdq2b/kFkttR2IH3758mVvb2/518loa2sbN25cenq6u7u7PPsXFhba29uPGDEC1rsfFOgsVfyVI79m1UmyqhOJmlarCul+sEf99a9/bWhoSEpKIqc5rQWdpYq/cuTXrDpJVnUiUdNq1THd83g8Op0+hO/DgHQ/KKo7xgIA0BL45D2gbLC4KEAIoWHDhlEdAjE05kSoAh2owSDdA4QQkjwPoe405kSoAh2owWDkC+np6RG+2hGNRhMKhcTWiVPNwUoC+1B5XacIJXW7el178iD5+lTNXweVBZ2lZuD6pgR0u5yU8QYmg2p+OFBZcBGrGcg7lIBuVxLpjoVOVjYYuwcAAK0A6R4AALQCpHsAANAK8JgVAEBLaduNZUj3AAAt1d3dTfK0UdLa6hMM5gAAgFaAdA8QQqjHV02pL405EapAByoI+98XT6ogSPcAIYRqa2upDoEYGnMiVIEOVNAvv/yyadMmqqPoG6R7tdHZ2VlaWooQamhogKdRSCMQCN6/f48Qqq2tJfO2HlBHAoFg165dCQkJVAfSN0j3qg7DsJ9//nnmzJkMBmPOnDkIIS8vL0dHx61bt1ZXV1MdnSZLT09fsmQJg8EIDg5GCPn7+9va2kZERLx584bq0ICKOn78uIeHx4wZM6gOpG9KTPcYhlVXVwsEAvyl5D9Afu/fv58yZcr3338fHR394cOHiooKhFBDQ0NaWpqJiYmPj8+xY8eojlEDcbncJUuWbNiwISwsrKKiorKyEiFUU1Pz5s0bHx8fFou1detWWKoF9NDW1nb48OGDBw9SHUi/lLVIRU5OzqFDh5YsWVJZWRkcHHzlypUlS5YM6k2Pz+fPmjVr4sSJx48fV0aEqq+kpGT69Onx8fFbt26VTOGSXlfk/fv3ixYtCg4OPnr0qIJtacxyJYqfCJvNDg4OnjZt2tGjR/X09HpXy+VyV61axefzb926JdlBY1C4pCX5FyGxLW7fvl0oFMr4Ckbqf8sU+V7z/ly9enXMmDGNjY34y2XLlunp6XV0dAyqksbGRjqd7unpqYQA1UB7e/vYsWOTk5N7lPf4kXG53PHjx586dUrB5pR0JZBPwRMRi8UsFmv79u2yqxWJRAsXLoyOjlakLdVE8pUg3Rz5FyGBLf773/+2srLC76uR0NzQEN98fX29qanp+fPnJSWrV68OCgqS3uHAgQPyVFVbW8vlcgmPUC3s27dv1apVvct7XzHFxcUMBqOlpUWR5ii/EImi4IlcuXJl0qRJ+NM3sqvlcrl2dnYFBQWKNKeCIN0PzdKlSw8ePEhac0NDfPNffPGFiYlJV1eXpMTFxeVPf/oThmF5eXm7du2KiIhgMpmEt6tJ+Hy+hYVFZWVl7019XjHr1q37+uuvFWmR8guRKAqeiJ+f3507d+Ss9tixYytXrlSkORUE6X4I8vLyHBwcBhzAoPy3jPjm7ezs5s+fL3mJzx558OCBpOTGjRuQ7mV78ODB1KlT+9zU5xWTlpYWGBioSIuUX4hEUeREamtrrayshEKhnNXW19dbWFj0ub/6gnQ/WGKxOCgoSHo8Q6nNKYLgmTmNjY11dXVBQUGSkkePHtFotMDAQGIb0mxFRUV+fn7y7+/r66uyD/Kpkbdv33p7e9No8i4kNXLkSFNTU3guSctdvny5o6NjxYoVVAcyMIKXSNPV1UUIjRkzRlKSlpbm6+trbGw8hNra2tqMjIz09fUH3NPJyQmfLadUNBqNnAdtDA0Nhw0bduTIkT639rfQkiILMOnq6ip7/SZyek+REzE3N//48eNgu5fJZA6tuUEh7doj4UroQUdHR3J2lC8i1hufz3/27Fl2dnZxcXFZWVlzczOPx9PV1aXT6ba2tkwm8969e7t27eLz+YaGhgPWpsgJKngNMJlMgtO9tbW1p6cnm83GX6anp6empsbGxg6hqpaWFkdHRzc3t/z8/AF3xieka4zDhw83NTV98803vTf1OZfr48ePDAaDx+OREp3GunfvXmJi4v3793tv6m8KnYuLy6+//urs7Kz86DQT5RMx+9skEonu3Llz8eLFO3fujBs3Ljg4OCQkZMOGDQwGw9jYWCwWCwSC+vr6pKQkc3Pzy5cvJyQkzJkz5/PPPw8LC5PxByLJJ9gD8QsgJycnx8bGWlpalpWVicVioVAYEhIyhHqMjIycnZ3d3NwIj1D1ubq6pqeny79/YWGh9F9UYGjGjBnz9u1bDMPk/AjG4XBaWlo++eQTZQcGyCQUCpOTk7/55hsbG5vIyMiTJ09aWFj0ueeIESMePXr06NEjDw+P1tbW69evJyYmxsTEbNu2bdOmTXQ6neTIB6aMGwJdXV2FhYV8Pj8lJYVGo7W3t0tvhVu1A2pvbzczM+tzbmWfP7L4+Phdu3YpPy7N5+bmlpeX17u8z25PSUkJCwtTflCaDKnYrdq0tDQ3NzcWi5WTkzPg4du2bdu8eXOPwmfPnoWFhY0ePfr27dsDNkcy5Ta/du1af3//HoXXrl0bNWqUUtvVABs3boyLi+td3vuKaWhosLKy6nPWJhisEydOsFis3uW9u10gEIwdO1Z6yhkYAtVJ952dnVu2bHFycrp586Y8x8p+rur+/fujR49ev349j8frszlKKHHNHLFY/PDhw2nTpklKiouL9+3bd+LECS6XGxcXl5iYqLzW1d2+ffsuXrz48OFD2bt1d3dHRERs2rTJ0dGRnMA02/r16+vq6k6cODHgnvHx8ePGjZs1axYJUQFlq6urCwwMbGpqKigoCAsLk+eQL7/8MiYmxsbGps+ts2fPLigoEAqFU6ZMUaGlDJX0NrJ3796QkBA9PT0PD4+YmBgltaLZMjIyGAzG3bt3pQulf2Q8Hm/x4sXz58/v/RQoGLLy8nI7O7sTJ05IF0p3u0gk2rFjh7u7O5vNJj06TYNU4NN9SUmJk5PTV199Jf+BeXl59vb28iwMk5iY6ODg8O7dO0wFPt0rq3npZ0807DkUMuXm5trb269du7a0tBQvwa+Yzs7OCxcuODs7b9iwgc/nUxqjBiovL58wYcLcuXOfPHmCl+Dd3t3dff/+fV9f31mzZim4agXAqUK6P3Xq1Llz5wZ1YFBQUEpKipw73759+8WLF5gGp3tAFA6Hs3v3bjs7O3d39zlz5ujr60+fPt3MzGzmzJmPHz+mOjqNJRAIkpKSxo4d6+DgMG/ePH19fRaLZW1tPXny5EuXLonFYqoD1BCqkO4H68aNGz4+PiKRiJzmCET1gpxAPhiGvXnzprS0tLi4eOrUqRMnTuxvchggVllZWUlJyZMnT6ZOnerp6QnTLolF+bz7IbT4/v17oVDo4uJCTnMEgnQPEEIoLi7u22+/pToKAmjMiVCF5A5Ux3SvLs31EQCke4BU4EIkisacCFXg6000prne4LtqAQBAK0C6BwAArQDpHgAAtALxS6QBAIBaoNFoZC65LP9XKSgrAGqbBwAAqgiFwkHt3+NeK+W3XgcLBnMAAEArQLoHAACtAOkeAAC0AqR7AADQCpDuAUJkfb82CTTmRKgCHajB1OzOMgBAk1C7iMJgwcwcdXL37t329naqowAAAApoUbrHMCwuLo7MpyoAAEB1aFG6z8rK8vHxMTExoToQAACggBal+/Pnz69atYrqKAAAgBpqdqthyLq6unx8fAoLC4cNG0Z1LACA/4JbtWSi5tN9WVlZRkbGhw8fSGvx1q1b8+bNg1wPANBaZKf7x48f+/r6Pnr0CCH0ww8/7NmzRywW97knn88PDg6Ojo4mpN3z589HREQQUhUAAKgjUlfEzMzMnDlz5rNnz7y9vRFCHA5n5cqVISEhM2bM6L0zh8N5+vRpW1ub4u02NzfX1dVNmDBB8aoAAEBdYSTy8PBgsViSl2lpaV5eXsXFxf3tX1tby+Vy5amZnEcBKV+uWnkxkHBq5PSe+vaPigRAyZlS3r2DpY69xGQyybvVUFNT4+DgEBsbS+b33OMCAgKuXr1qZ2dHcrsAANngVi2ZyBu7LysrQwiNHDmStBZxJSUlpqamkOsBAFqOvHQ/ZswYhFB1dTVpLeJguj0AACBE7tj9+PHjp02bJl3C5/Nv3rzZ3/6tra1dXV3y1Axj96pZLclNKK8Vyn/0MHavUtSxl0gdu0cIPX36dOrUqTdv3pw7dy5ecvz48YCAgMmTJ/feuaWlxdHR0c3NLT8/f8gtZmZmnjlz5ty5c0OuAQCgPDB2TyZS36P8/f2zs7M3b96ck5ODz8nx8PDoM9cjhIyMjJydnd3c3BRpsb+RnJycnNzc3Kampqqqqp07d+ITQwEAQINR8+7U2NjIZrNdXV2Vuj4ln8/HF07Q1f2/WxRisXjy5Mk5OTmGhoZPnz5duHBhXV1dj30AACSAT/dkombIzMbGxsbGRtmt3Lx5MzQ0tHce19XVXb16NT4QZmtr29zc3NnZaWxsrOx4AACAQmr27jQoCxYsOHjwoKenp4x9oqKi7Ozsdu/eTVpUAAAJ+HRPJjW7IS6/5ubmhoYGGbleLBbv378/ODh4+fLlZAYGAACU0NgB69TUVBl5vKOj49ChQxEREeHh4YcPH+7q6iIzNgAAIJ+a/TEiPxaLdfbs2T4f4hUKhf7+/gUFBfhLV1fXkpIScqMDACAEgznkUrNw5cflck1NTamOAgAgC6R7MqlZuAAATQLpnkwaO3YPAABAGqR7AADQCpDuAQBAK0C6BwAArQDpHgAAtAKkewAA0AqQ7gEAQCtAugcAAK0A6R4AALQCpHsAANAK2pXu7969297eTnUUAABAAS1K9xiGxcXFKfXrEgEAAxIIBHv37uXxeP3twOPx9u7dKxAIyIyqP+oVrWxalO6zsrJ8fHxMTEyoDgQArUan06urq8PCwvrbISwsrLq6mk6nkxlVf9QrWtm0KN2fP39+1apVVEcBAECJiYlsNnvbtm29N23bto3NZicmJpIfVX/UK1oZ1GwBzyHr6ury8fEpLCwcNmwY1bEAAFBlZaW3tzebzZZeADk9Pf2zzz4rKChgMpnUhtdD72gRQjo6Oubm5ioYbX90RSJRc3Pzhw8fEELd3d3l5eWtra2SzQKBoKqqSiQS9XmwUCh8/vz527dvxWJxj03V1dXp6enl5eW93044HM7Tp08zMzOFQuH79+8JPZ1+3bp1a968eZDrAVARTCYzISGBRqOx2Wy8ZNSoUWvWrElISFDB7Nk7WjabTaPRVDPa/gx7+PDhH/7wh/b2dhMTk0uXLnV1df3lL3/59ddfw8LCkpOTc3NzW1pa1q1bJxKJfH19pY88ffr0/v37/fz8ioqKoqKi3N3dHRwcEELd3d07duxob28fNWrUvXv3du/eHRQUZGFhgR915MiRwsJCd3d3DMPOnDnzxRdf7Nixo8/I+Hz+9OnTX758OW/ePMXP849//OP27dttbGwUrwoAQIgpU6akp6c3NDSEhIQghDgcDofD+eGHH6iOq289oj106BCGYSobbd8wDAsKCpo/f/61a9cwDMMwrKysDCG0bdu2169f4yVnz541NjZubW3F/ufkyZP29vZcLhd/+eDBgxEjRuB/6Zw5c8bR0bG4uBjfFBsb6+3tLRaLMQwrKSkJCQnBpEydOhXrR2NjI51O9/T07G8H+TU1NU2aNEnxegAAxCooKNDR0SkvLy8vL9fR0SkoKKA6IlnUK9reEIZhLBbL2toaz8gYholEIjqdvmjRIslOT548QQjl5eXhLzkcjrm5+datWyU7iMViKyurkydPYhh29epVCwuLFy9e4Jv++c9/IoR+++03DMMyMzPNzc1fvXolOfDvf/+7jOBqa2sl7yiykfP3FI1GI6EVSmIg4dTI6T317R8VCYCSM9XVVbM5I5QErOCPhslk/vd4d3d3yYR0XV1dOp3u4eEh2c/AwAAhJJlY+vLlSzabTafTc3JyJPtYWVmVlpYihBYvXrx48WKEUGVl5Zs3b/Ly8hBCHA4HIeTn58dkMr28vDw9PWfNmhUaGrpu3ToZ8dnZ2cl5JhUVFTK2BgQEXL16Vf7aAACkefXqFT48kpGR4eXlRXU4A1CvaHv473uUoaFhjw29S7D/3XStqqpCCDEYDAMpFy5c+PLLL/HdTp06FRgYeOHCBUtLy6CgIEkN+vr6jx8/3rdvn56eXlJS0owZMyIjIzElTw0qKSkxNTWFXA+AavLy8rK1tbW1tVWL7Kle0fYwlL8O3NzcEEKWlpaTJk3qvXXHjh2nTp0qKChwdnZGCEn+AsAwrLy83NTUNCEhISEh4ePHj8nJyTExMevWrQsODlbgFAYA0+0BUHG3b9+mOoRBUK9opQ1lBGrixImffPJJWlqadKFAILh16xaPxzt69GhkZCSe6xFC+BRPhNCDBw/+9re/paSk4C+NjIy2bNmydOnSoqKi/hpqa2vj8/lDiFACw7B//OMfn332mSKVAACUysXFxcXFheoo5KVe0UqjIYSEQmGPUqFQKF2I/1+SefX19U+fPh0eHl5YWDh+/Hi88NixY7NmzdLT0zM0NOzu7pYcW1ZWRqfTOzs729vbLSwsTp48GRUVJRkp4vP5U6ZM6TOylpYWR0dHNze3/Pz8AU/DycmpsrKyv61ELZxAo9GkT40SSoqBhFMjp/fUt39UJABKzpTy7h0sdewlJpOps3z58vv37+vo6MyePTsmJqa0tPTnn3/OzMy0srIKCgpKSko6cuTI/fv33717Z29vP2PGjO+//x4/OCMjY9euXZ9++umECRMKCgqCg4Pnzp2LELp27dr27duXLVsWEhLy6tWrFStWfPfdd48fP164cOGkSZNOnz7t4uISFBRkZmaWlZVla2sbHR3dZ3CdnZ3+/v5eXl6SPwiGYP369eHh4TNnzuxRnpOTk5ub29TUVFVVtXPnTm9v7yE3AQAAakHRRRTq6up4PJ6rq6t0oVgsLi4uFgqF7u7uenp6CCEul2tqatre3q6vr0+n00tLSwUCwejRo/E5P0rC5/PxhRN6zJoSi8WTJ0/OyckxNDR8+vTpwoUL6+rq1G4qGAAADIqic2z7nPGiq6vr7u4uXWJqaooQGj58OP6yx9uDkty8eTM0NLR3HtfV1V29ejU+idXW1ra5ubmzs9PY2JiEkAAAgCqavETaggULDh486OnpKWOfqKgoOzu73bt3kxYVAABQgvrHRJWkubm5oaFBRq4Xi8X79+8PDg5evnw5mYEBAAAlNHbAOjU1VUYe7+joOHToUERERHh4+OHDh7u6usiMDQAAyKexgzksFuvs2bMjR47svUkoFPr7+xcUFOAvXV1dS0pKyI0OAADIprHpHp8LRHUUAIB+NTQ0XL58eeTIkU1NTWFhYaNGjaI6IlkqKiqysrJOnz59+fJlW1tbqsMZCo0dzIFcD4CKi4yMXLp06bJly5YvX75mzRqqwxmAgYHB7Nmz2Wy2WnwLeZ80Nt0DAFRZa2trfn4+PpPb0tKyrKyspqaG6qBksbW17XNwWI1AugcAUKC2tlb6T3Bzc3MVT/caANI9AIACJiYmPB5P8pLH4xG1thXoD6R7AAAFnJycjI2N8XFw/Kv08JXVgfIMS0hIoDoGAIA28vDwuHz5sqWlZUpKyooVK3qsvKJqXr58eeHChYcPH3Z0dHC5XOnv+1MX/wGv9TUhx23A5AAAAABJRU5ErkJggg==)\n",
+        "\n",
+        "In this tutorial, we will learn how to implement superdense coding using the `torchquantum` library. We will cover the following steps:-\n",
+        "\n",
+        "### **Step 1 - Preparing the entangled state/ 2 qubit bell pair.**\n",
+        "\n",
+        "First, a bell pair of two qubits is created. Where q0 represents the sender's qubit and q1 represents the receiver's qubit. A hadamard gate is used to place q0 in a superposition of states. Then, with q0 as the control and q1 as the target, a CNOT operation is conducted.\n",
+        "\n",
+        "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKgAAABWCAIAAADUsUqKAAAKx0lEQVR4nO2dW0zTXhzHD7MMHAgoN53yZyojAhMFBAUyIYJKFPEFhRcRiMSIEFR8wBsGo2A04pXwhCh7kBcTAolE5CoOUC4monKRBBBxgw0Ex2Bc1vN/qJmTjW3Atna0nwfS/mhPv9n39PS0Pb9TMwghMDXMzc3n5ub0WyaCILOzs/otk8iYmaLxZmb6l22IMokMDW8BFPhAGU9SKONJCmU8SUEUSxDCHz9+ODs70+l0AMDMzAy2YCpIJJLh4WE6nU6j0SCEcrkcQshisaRSqUAgsLCwMDMzAwDI5XK5XL5lyxa89eLMnzOez+dHRUVVVVVlZ2dXV1cnJyfX19cvtqzp6em9e/empqbqW6ROCIXCFy9eJCYmbtq0ydPTMz8/v7q6GgAgFouLi4tPnz7t4uLCZrPz8vIqKipwUUgsIIQvX75ks9lDQ0MQQgjh8ePHzc3NpVIpXCRDQ0N0Op3D4Sx2x8WCyVZLS0sLACAyMnJevLOzEwAQGhq6hDJXJIhQKExISMjLy3NycsKqAoPBCAgIYDAYi61DTk5Ovb29a9as0WfFXCSWlpaKv7rEDc3IyMjo6Kibmxt2oSEOtGvXrqEoeuzYMUWovr4+JCQEW25pabl58+b9+/d1bB6ZTCa+xhOHr1+/BgYGOjg4uLu7Ozo6Pnv2DG9F/8JkMg8fPqxoAQYGBgAAFRUVEMLW1tbo6OjZ2VkIYWZmZnZ2Nm4N07+AhZvlz58/AwCio6Pnxfv6+gAAERERSyhzCQgEgnXr1s37qQsLC/V4iGUCAADKjj5//hxBkImJCQihv79/ZWUlFpfJZLa2tuPj4/jI/BfiG5+SkqJ6jtnb26MoqsejLAcEAMBmsxXiKisr/f39raysxsbGWlpatm7disUtLCxsbW0rKiqio6M1tB+/fv1iMBgWFhaam5nNmzdjThiIDx8+nDhxQjkyMTGhdS9DX4ZHRkZoNL09OEEQZMlvqlgsFsLhcMbGxrD1mpqa4uLi9PR0AEB3dzeEULmLx2AwhEKhhuJGRkZcXV09PDyam5s1H7i3t3dpijG0OhQQEMDj8ZQj/f39JSUlmveC+ntJExER8fr1a9W4WCy2t7fX11GWA/L06dP09HR7e/uenh4URWdnZ0NDQwEA09PT4N+fGEXR8fFxDWUxGIwtW7Z4eHgYWjTxiYyMVDU+KCiIIK4DAGj+/v5v3rxhs9lpaWlMJhNBkODgYACAjY0NAEC5MRkfH9fcY1+9evWnT5+KiooMLZr4JCcnHzhwQDni4OBQUFCAlx5VaAAACwsLDodDp9Nra2v9/Pysra0BAB4eHpaWliKRSLHp2NiYr68vbkpNChqNVl5e/uTJEwBAYGDguXPnvnz5sm3bNrx1/eVvXwNF0aqqKi6Xi63S6fSYmJi2tjZstbe3l8ViBQQE4KDRNKHRaGfPngUANDQ03L9/X/F8jCD8eUlz/fr1t2/f/vz5s7y8HEXRe/fuAQAePHgQGxtrZ2fn6OiYm5vL4/GI/9oG64X8/v1bbVxzH4VU/BlvNDc3hyB/KoHyslwu//jx49TUlI+PD3YJIAJqh0m1tbU9fPiwo6Oju7vb3Nw8PDyczWbfuHHjy5cvd+/e7erq6ujooNFo+/fvd3V1vXPnji5lGkgqESCoLM2Y0Jg7whpPDcQgKZTxJIUynqRQxpMUyniSgmjfhHggCKL3N2mKO1iSQNCbjRUDdTtHQSwo40kKZTxJoYwnKZTxJIUynqRQxpMUynj9MzU1lZeXFx4evnbtWgAAnU5ns9mpqakfP37EW9pf/hoPIRwYGJiZmcFWFQsUi6KsrMzd3b22tjYtLQ0bRS6VSktLSzdu3HjkyJGEhARdRvgbAyyv4t27d5GRkYWFhdevX6+qqjpz5owih0Z3ZDIZl8tNSUnRQ6KHafLo0SMXFxc+n6+IAKUEnYmJiaSkpB07dohEIjzU/YNJpkkTk7KyMhcXl+/fvysHgUpm1qVLl0JCQrCMRBwBAoHAxsaGx+MpQvHx8cHBwYpVgUBw69YtHYsbHBz8/fu3njWaApOTk/POdQxV4+VyeVhYWH5+vrGkqQecOnXK2tpaJpMpQlu3br18+TKEsLGx8cqVK3FxcSwWCz+FpsGTJ09UMzXhArmYbW1tmzZtwiZrwQtNadIYJSUllPFaCQsLKy0tVY2rNR5C6O3t3djYaGBRmqD9/PkTy5nCqK6uRhAkKCgIj46mCdPa2qrIRdEFLpfb2tpqOD1aWTBNemnFESRNWsFycokXC3bXroraMSPW1tYoiqpNo9cRQ6VJLwGjpUkTDQihpaWlRCJRzTRaaCDG+fPnbWxssrKyjCJQDQumSS8B0qZJm5mZ/ffffz09PZ6enjru0t/fHxMTY1BVWoAQymSy9vb26enpoqIiBEEkEolyL4Dq3OlCSkpKTk6Oahyo69xJpVI7OzuxWGx4XQuyYJq0AhRFURTFp1aaDomJiY8fP5ZKpbps/Pjx4/379+M8SYKiCsjlcldX14sXLyoiHR0dmZmZYWFhdnZ26enpubm5eFRNk+HkyZNJSUnzgkDljG9vb3dycvr27ZuxdKnnj6zMzMzQ0FBzc3MvL68LFy7gq8lEkUgk3t7eGRkZyk9m5hnf3t7u6ur64sULo6ubzx9Zyo+OcX+MbLqIRKKQkJB9+/a1trZiEYXxUqk0JyfHycmJCK5DbLoz8G86AdlSC/SIg4NDZWVlQUHB0aNH165dy+Vyra2tz58/39fXV1NTExERwefz3dzc8JYJAJVQYSBQFG1paWlubq6pqeFwOJ6enuHh4apzXeIIZTxJoYZekRTKeJJCGU9SKONJCmU8SaGMJymU8SSFMp6kUMaTFMp4kkJ048vLyyUSCd4qViCEflYPIeRwOO/fvyfOxNkrBkKf8fX19b6+vpTrhoDQxvN4vHlfEaPQF8Rt6mUyma+vb3t7+6pVq/DWsgLR9Yzv6empra0dHh42qBplysrKDh06RLluILQbX1dX5+/vj32LPT8/H/sIsdot9fv9eB6PFxcXp5eiKNSgeUheXV0dgiBtbW3YaklJiZWV1UKTZehxYgSRSOTn57f8cigWQovxXl5eyh/irays3LlzZ2dn50Lb6zgxAovFMk61xn3gqOEELKdkFoulyXgsVz49PV2rkXpnz549g4ODxj8uedB0je/p6QEArF+/fsk1a2l0dXXZ2NgwmUwjH5dUaDIey5vHzntjQt2+GwPNDcL27du5XK5yZHp6Wu2cHxijo6PK0+ksBHWNx7dkLdd4CGFTUxOCIK9evVJEHj161NzcrHZjsVhsZWW1a9curcZroK6uLi4ubjklUOiCllqze/fud+/enT17ls/nY/15Ly+vXbt2qd1YLxMjLNTO8/n8hoYGkUjU39+fkZHh4+OznKNQaDnjFQiFws7OThRFDVoNZTKZp6en6jxgcrncx8dncnISQtjU1OTs7IzvXGErAF2vE87Ozs7OzgatggCA0tLSyMhIGm1+l5NGo8XHx2NXtQ0bNojF4qmpqSXP0EQBiPaSJioqKjs7m8PhaNgmOTmZyWRevXrVaKpWJATKiBaLxUKhUIPrKIpmZWXt3bs3NjbWmMJWJAR6H19cXKzBUalUevv27bi4uJiYmDt37shkMmNqW4Hg3cn4y8GDBwUCgdp/zczMKHfj3d3djaxt5fE/mu1tqHIt0joAAAAASUVORK5CYII=)\n",
+        "\n",
+        "\n",
+        "### **Step 2 - Encoding the message**\n",
+        "\n",
+        "Rules:\n",
+        "\n",
+        "\n",
+        "a) For sending 00 - Perform no operation. \n",
+        "\n",
+        "b) For sending 01 - Perform a Pauli-Z operation where q1s state is flipped.\n",
+        "\n",
+        "c) For sending 10 - Apply a Pauli-X gate. \n",
+        "\n",
+        "d) For sending 11 - Apply a Pauli-Z gate followed by a Pauli-X gate.\n",
+        "\n",
+        "### **Step 3 - Decoding the message**\n",
+        "\n",
+        "Bob gets Alice's qubit (leftmost qubit) and decodes the message sent. He does not need to know the state to decode it; instead, he simply applies the restoration operation. Bob employs a CNOT gate, with the leftmost qubit serving as control and the rightmost serving as target. Then he uses a Hadamard gate and eventually measures both qubits to extract the sent message.\n",
+        "\n",
+        "\n",
+        "![image.png](data:image/png;base64,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)\n",
+        "\n",
+        "\n",
+        "### **Step 4 - Measurement**\n",
+        "\n",
+        "In superdense coding, measurement plays a crucial role in extracting the classical bits that Alice encoded. After Bob has applied the decoding gates, he performs a measurement on both qubits to extract the information.\n"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "MLrQMHvMOBFB"
+      },
+      "source": [
+        "## Installation"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "dW_Cy1QyXbcB",
+        "outputId": "796f970d-f660-453a-f1a2-ae3b84b711b7"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Cloning into 'torchquantum'...\n",
+            "remote: Enumerating objects: 12494, done.\u001b[K\n",
+            "remote: Counting objects: 100% (765/765), done.\u001b[K\n",
+            "remote: Compressing objects: 100% (345/345), done.\u001b[K\n",
+            "remote: Total 12494 (delta 423), reused 674 (delta 392), pack-reused 11729\u001b[K\n",
+            "Receiving objects: 100% (12494/12494), 74.92 MiB | 31.26 MiB/s, done.\n",
+            "Resolving deltas: 100% (6780/6780), done.\n"
+          ]
+        }
+      ],
+      "source": [
+        "!git clone https://github.com/mit-han-lab/torchquantum.git"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "7QP0AcjaFdko",
+        "outputId": "8fd5df5d-1bab-4ba8-e0a1-cf1423918f5f"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "/content/torchquantum\n"
+          ]
+        }
+      ],
+      "source": [
+        "%cd torchquantum"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "k0v6FtlqFfyh",
+        "outputId": "892b70e9-ec49-4330-bb64-40b9dce70b9a"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
+            "Obtaining file:///content/torchquantum\n",
+            "  Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+            "Requirement already satisfied: numpy>=1.19.2 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (1.22.4)\n",
+            "Requirement already satisfied: torchvision>=0.9.0.dev20210130 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (0.15.2+cu118)\n",
+            "Requirement already satisfied: tqdm>=4.56.0 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (4.65.0)\n",
+            "Requirement already satisfied: setuptools>=52.0.0 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (67.7.2)\n",
+            "Requirement already satisfied: torch>=1.8.0 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (2.0.1+cu118)\n",
+            "Collecting torchpack>=0.3.0 (from torchquantum==0.1.7)\n",
+            "  Downloading torchpack-0.3.1-py3-none-any.whl (34 kB)\n",
+            "Collecting qiskit==0.38.0 (from torchquantum==0.1.7)\n",
+            "  Downloading qiskit-0.38.0.tar.gz (13 kB)\n",
+            "  Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+            "Requirement already satisfied: matplotlib>=3.3.2 in /usr/local/lib/python3.10/dist-packages (from torchquantum==0.1.7) (3.7.1)\n",
+            "Collecting pathos>=0.2.7 (from torchquantum==0.1.7)\n",
+            "  Downloading pathos-0.3.0-py3-none-any.whl (79 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m79.8/79.8 kB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting pylatexenc>=2.10 (from torchquantum==0.1.7)\n",
+            "  Downloading pylatexenc-2.10.tar.gz (162 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m162.6/162.6 kB\u001b[0m \u001b[31m15.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25h  Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+            "Collecting dill==0.3.4 (from torchquantum==0.1.7)\n",
+            "  Downloading dill-0.3.4-py2.py3-none-any.whl (86 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.9/86.9 kB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting qiskit-terra==0.21.2 (from qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading qiskit_terra-0.21.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.7 MB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.7/6.7 MB\u001b[0m \u001b[31m58.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting qiskit-aer==0.11.0 (from qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading qiskit_aer-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (19.2 MB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m19.2/19.2 MB\u001b[0m \u001b[31m58.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting qiskit-ibmq-provider==0.19.2 (from qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading qiskit_ibmq_provider-0.19.2-py3-none-any.whl (240 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m240.4/240.4 kB\u001b[0m \u001b[31m22.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-aer==0.11.0->qiskit==0.38.0->torchquantum==0.1.7) (1.10.1)\n",
+            "Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.10/dist-packages (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (2.27.1)\n",
+            "Collecting requests-ntlm>=1.1.0 (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading requests_ntlm-1.2.0-py3-none-any.whl (6.0 kB)\n",
+            "Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (1.26.15)\n",
+            "Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (2.8.2)\n",
+            "Requirement already satisfied: websocket-client>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (1.5.1)\n",
+            "Collecting websockets>=10.0 (from qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (129 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m129.9/129.9 kB\u001b[0m \u001b[31m13.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting retworkx>=0.11.0 (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading retworkx-0.12.1-py3-none-any.whl (10 kB)\n",
+            "Collecting ply>=3.10 (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading ply-3.11-py2.py3-none-any.whl (49 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: psutil>=5 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7) (5.9.5)\n",
+            "Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7) (1.11.1)\n",
+            "Collecting stevedore>=3.0.0 (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading stevedore-5.1.0-py3-none-any.whl (49 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m5.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting tweedledum<2.0,>=1.1 (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading tweedledum-1.1.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (929 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m929.7/929.7 kB\u001b[0m \u001b[31m59.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting symengine>=0.9 (from qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading symengine-0.10.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (37.4 MB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m37.4/37.4 MB\u001b[0m \u001b[31m18.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (1.0.7)\n",
+            "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (0.11.0)\n",
+            "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (4.39.3)\n",
+            "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (1.4.4)\n",
+            "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (23.1)\n",
+            "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (8.4.0)\n",
+            "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.3.2->torchquantum==0.1.7) (3.0.9)\n",
+            "Collecting ppft>=1.7.6.6 (from pathos>=0.2.7->torchquantum==0.1.7)\n",
+            "  Downloading ppft-1.7.6.6-py3-none-any.whl (52 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m52.8/52.8 kB\u001b[0m \u001b[31m6.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hINFO: pip is looking at multiple versions of pathos to determine which version is compatible with other requirements. This could take a while.\n",
+            "Collecting pathos>=0.2.7 (from torchquantum==0.1.7)\n",
+            "  Downloading pathos-0.2.9-py3-none-any.whl (76 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.9/76.9 kB\u001b[0m \u001b[31m7.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25h  Downloading pathos-0.2.8-py2.py3-none-any.whl (81 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m81.7/81.7 kB\u001b[0m \u001b[31m9.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting multiprocess>=0.70.12 (from pathos>=0.2.7->torchquantum==0.1.7)\n",
+            "  Downloading multiprocess-0.70.14-py310-none-any.whl (134 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m134.3/134.3 kB\u001b[0m \u001b[31m13.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting pox>=0.3.0 (from pathos>=0.2.7->torchquantum==0.1.7)\n",
+            "  Downloading pox-0.3.2-py3-none-any.whl (29 kB)\n",
+            "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch>=1.8.0->torchquantum==0.1.7) (3.12.0)\n",
+            "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.10/dist-packages (from torch>=1.8.0->torchquantum==0.1.7) (4.5.0)\n",
+            "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch>=1.8.0->torchquantum==0.1.7) (3.1)\n",
+            "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch>=1.8.0->torchquantum==0.1.7) (3.1.2)\n",
+            "Requirement already satisfied: triton==2.0.0 in /usr/local/lib/python3.10/dist-packages (from torch>=1.8.0->torchquantum==0.1.7) (2.0.0)\n",
+            "Requirement already satisfied: cmake in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch>=1.8.0->torchquantum==0.1.7) (3.25.2)\n",
+            "Requirement already satisfied: lit in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch>=1.8.0->torchquantum==0.1.7) (16.0.5)\n",
+            "Requirement already satisfied: h5py in /usr/local/lib/python3.10/dist-packages (from torchpack>=0.3.0->torchquantum==0.1.7) (3.8.0)\n",
+            "Collecting loguru (from torchpack>=0.3.0->torchquantum==0.1.7)\n",
+            "  Downloading loguru-0.7.0-py3-none-any.whl (59 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m60.0/60.0 kB\u001b[0m \u001b[31m5.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting multimethod (from torchpack>=0.3.0->torchquantum==0.1.7)\n",
+            "  Downloading multimethod-1.9.1-py3-none-any.whl (10 kB)\n",
+            "Requirement already satisfied: pyyaml in /usr/local/lib/python3.10/dist-packages (from torchpack>=0.3.0->torchquantum==0.1.7) (6.0)\n",
+            "Requirement already satisfied: tensorboard in /usr/local/lib/python3.10/dist-packages (from torchpack>=0.3.0->torchquantum==0.1.7) (2.12.2)\n",
+            "Collecting tensorpack (from torchpack>=0.3.0->torchquantum==0.1.7)\n",
+            "  Downloading tensorpack-0.11-py2.py3-none-any.whl (296 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m296.3/296.3 kB\u001b[0m \u001b[31m26.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: toml in /usr/local/lib/python3.10/dist-packages (from torchpack>=0.3.0->torchquantum==0.1.7) (0.10.2)\n",
+            "INFO: pip is looking at multiple versions of multiprocess to determine which version is compatible with other requirements. This could take a while.\n",
+            "Collecting multiprocess>=0.70.12 (from pathos>=0.2.7->torchquantum==0.1.7)\n",
+            "  Downloading multiprocess-0.70.13-py310-none-any.whl (133 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m133.1/133.1 kB\u001b[0m \u001b[31m13.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25h  Downloading multiprocess-0.70.12.2-py39-none-any.whl (128 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m128.7/128.7 kB\u001b[0m \u001b[31m14.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.0->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (1.16.0)\n",
+            "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (2022.12.7)\n",
+            "Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (2.0.12)\n",
+            "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (3.4)\n",
+            "Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy>=1.3->qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7) (1.3.0)\n",
+            "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch>=1.8.0->torchquantum==0.1.7) (2.1.2)\n",
+            "Requirement already satisfied: absl-py>=0.4 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (1.4.0)\n",
+            "Requirement already satisfied: grpcio>=1.48.2 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (1.54.0)\n",
+            "Requirement already satisfied: google-auth<3,>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (2.17.3)\n",
+            "Requirement already satisfied: google-auth-oauthlib<1.1,>=0.5 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (1.0.0)\n",
+            "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (3.4.3)\n",
+            "Requirement already satisfied: protobuf>=3.19.6 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (3.20.3)\n",
+            "Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (0.7.0)\n",
+            "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (1.8.1)\n",
+            "Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (2.3.0)\n",
+            "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.10/dist-packages (from tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (0.40.0)\n",
+            "Requirement already satisfied: termcolor>=1.1 in /usr/local/lib/python3.10/dist-packages (from tensorpack->torchpack>=0.3.0->torchquantum==0.1.7) (2.3.0)\n",
+            "Requirement already satisfied: tabulate>=0.7.7 in /usr/local/lib/python3.10/dist-packages (from tensorpack->torchpack>=0.3.0->torchquantum==0.1.7) (0.8.10)\n",
+            "Requirement already satisfied: msgpack>=0.5.2 in /usr/local/lib/python3.10/dist-packages (from tensorpack->torchpack>=0.3.0->torchquantum==0.1.7) (1.0.5)\n",
+            "Collecting msgpack-numpy>=0.4.4.2 (from tensorpack->torchpack>=0.3.0->torchquantum==0.1.7)\n",
+            "  Downloading msgpack_numpy-0.4.8-py2.py3-none-any.whl (6.9 kB)\n",
+            "Requirement already satisfied: pyzmq>=16 in /usr/local/lib/python3.10/dist-packages (from tensorpack->torchpack>=0.3.0->torchquantum==0.1.7) (23.2.1)\n",
+            "Requirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (5.3.0)\n",
+            "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (0.3.0)\n",
+            "Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (4.9)\n",
+            "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from google-auth-oauthlib<1.1,>=0.5->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (1.3.1)\n",
+            "Requirement already satisfied: cryptography>=1.3 in /usr/local/lib/python3.10/dist-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (40.0.2)\n",
+            "Collecting pyspnego>=0.1.6 (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading pyspnego-0.9.0-py3-none-any.whl (132 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m132.7/132.7 kB\u001b[0m \u001b[31m11.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting rustworkx==0.12.1 (from retworkx>=0.11.0->qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading rustworkx-0.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m54.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hCollecting pbr!=2.1.0,>=2.0.0 (from stevedore>=3.0.0->qiskit-terra==0.21.2->qiskit==0.38.0->torchquantum==0.1.7)\n",
+            "  Downloading pbr-5.11.1-py2.py3-none-any.whl (112 kB)\n",
+            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m112.7/112.7 kB\u001b[0m \u001b[31m11.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+            "\u001b[?25hRequirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.10/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (1.15.1)\n",
+            "Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /usr/local/lib/python3.10/dist-packages (from pyasn1-modules>=0.2.1->google-auth<3,>=1.6.3->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (0.5.0)\n",
+            "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<1.1,>=0.5->tensorboard->torchpack>=0.3.0->torchquantum==0.1.7) (3.2.2)\n",
+            "Requirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.19.2->qiskit==0.38.0->torchquantum==0.1.7) (2.21)\n",
+            "Building wheels for collected packages: qiskit, pylatexenc\n",
+            "  Building wheel for qiskit (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+            "  Created wheel for qiskit: filename=qiskit-0.38.0-py3-none-any.whl size=12128 sha256=4f7f0a0ad10fc979b9d35c9ab5851f0edc5c4661e807161ab1b13170575857c8\n",
+            "  Stored in directory: /root/.cache/pip/wheels/9c/b0/59/d6281e20610c76a5f88c9b931c6b338410f70b4ba6561453bc\n",
+            "  Building wheel for pylatexenc (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+            "  Created wheel for pylatexenc: filename=pylatexenc-2.10-py3-none-any.whl size=136820 sha256=27ea7bd84314063cf13f0bc9b2890f6314fa9969d41ba1bd4ac5be1c7ccae66c\n",
+            "  Stored in directory: /root/.cache/pip/wheels/d3/31/8b/e09b0386afd80cfc556c00408c9aeea5c35c4d484a9c762fd5\n",
+            "Successfully built qiskit pylatexenc\n",
+            "Installing collected packages: pylatexenc, ply, websockets, tweedledum, symengine, rustworkx, ppft, pox, pbr, multimethod, msgpack-numpy, loguru, dill, tensorpack, stevedore, retworkx, multiprocess, qiskit-terra, pyspnego, pathos, requests-ntlm, qiskit-aer, qiskit-ibmq-provider, qiskit, torchpack, torchquantum\n",
+            "  Running setup.py develop for torchquantum\n",
+            "Successfully installed dill-0.3.4 loguru-0.7.0 msgpack-numpy-0.4.8 multimethod-1.9.1 multiprocess-0.70.12.2 pathos-0.2.8 pbr-5.11.1 ply-3.11 pox-0.3.2 ppft-1.7.6.6 pylatexenc-2.10 pyspnego-0.9.0 qiskit-0.38.0 qiskit-aer-0.11.0 qiskit-ibmq-provider-0.19.2 qiskit-terra-0.21.2 requests-ntlm-1.2.0 retworkx-0.12.1 rustworkx-0.12.1 stevedore-5.1.0 symengine-0.10.0 tensorpack-0.11 torchpack-0.3.1 torchquantum-0.1.7 tweedledum-1.1.1 websockets-11.0.3\n"
+          ]
+        }
+      ],
+      "source": [
+        "!pip install --editable ."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "l72ZuDsXGHGk",
+        "outputId": "6e6668bd-6657-4a4d-c2e3-ec993daadf0b"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "env: PYTHONPATH=.\n"
+          ]
+        }
+      ],
+      "source": [
+        "%env PYTHONPATH=."
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "zRmn6K6DOL-n"
+      },
+      "source": [
+        "## Importing modules\n",
+        "\n",
+        "\n"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "YkPdVBrnFjay"
+      },
+      "outputs": [],
+      "source": [
+        "import math\n",
+        "\n",
+        "import torch\n",
+        "import torch.nn as nn\n",
+        "import torch.nn.functional as F\n",
+        "from torch.nn.utils.rnn import pad_sequence\n",
+        "import torchquantum as tq\n",
+        "import torchquantum.functional as tqf\n",
+        "import argparse\n",
+        "import tqdm\n",
+        "import time"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "f5ueV7ixQWX6"
+      },
+      "source": [
+        "##Step 1: Preparing the 2-qubit bell pair\n",
+        "\n",
+        "To prepare an entangled state, we use the Hadamard gate (h) and the CNOT gate (cx). The Hadamard gate (h) is applied to the first qubit (qubit 0) to put it \n",
+        "into a superposition of |0⟩ and |1⟩. This creates an entangled state between the two qubits. The controlled-X gate (cx) is applied with the first qubit as the control and the second qubit as the target. It entangles the qubits further and prepares them for superdense coding. Let's wrap this all up in a function.\n"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "FT4yfAg2Fjge"
+      },
+      "outputs": [],
+      "source": [
+        "def bell_pair():\n",
+        "  qdev = tq.QuantumDevice(n_wires=2, bsz=1, device=\"cpu\")\n",
+        "  qdev.h(wires=0)\n",
+        "  qdev.cnot(wires=[0, 1])\n",
+        "  return qdev"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "CVax-ADHRly1"
+      },
+      "source": [
+        "## Step 2: Encoding the message\n",
+        "Here, we develop a function to encrypt any message with classical bits 00, 01, 10 and 11 that Alice wishes to transmit over the channel."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "h2nTeMzzFjjS"
+      },
+      "outputs": [],
+      "source": [
+        "def encode_message(qdev, qubit, msg):\n",
+        "    if len(msg) != 2 or not set(msg).issubset({\"0\",\"1\"}):\n",
+        "        raise ValueError(f\"message '{msg}' is invalid\")\n",
+        "    if msg[1] == \"1\":\n",
+        "        qdev.x(wires=qubit)\n",
+        "    if msg[0] == \"1\":\n",
+        "        qdev.z(wires=qubit)\n",
+        "    return qdev"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "dhEp1xzEXDBk"
+      },
+      "source": [
+        "## Step 3 - Decoding the message\n",
+        "Once Alice has encoded her message, she sends her qubit to Bob. Bob can then decode the message by applying the corresponding gates to his qubit. Bob applies a controlled-X gate (cx) with the first qubit as the control and the second qubit as the target. This operation \"transfers\" the encoded message from Alice's qubit to Bob's qubit. Then, Bob applies a Hadamard gate (h) to the first qubit. This allows Bob to extract the original classical bits."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "-_QvwabmH0nK"
+      },
+      "outputs": [],
+      "source": [
+        "def decode_message(qdev):\n",
+        "    qdev.cx(wires=[0, 1])\n",
+        "    qdev.h(wires=0)\n",
+        "    return qdev"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "0tGZQCYQXtkg"
+      },
+      "source": [
+        "Putting all these functions together"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "NgWcSzf_IBOM"
+      },
+      "outputs": [],
+      "source": [
+        "qdev = bell_pair() # Creating the entangled pair between Alice and Bob\n",
+        "\n",
+        "message = '10'\n",
+        "qdev = encode_message(qdev, 1, message) # Encoding the message at Alice's end\n",
+        "\n",
+        "qdev = decode_message(qdev) # Decoding the original message at Bob's end"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "BMVr6t2jZKtY"
+      },
+      "source": [
+        "## Step 4 - Measurement \n",
+        "Finally, Bob can measure the qubits to extract the encoded message. The `tq.measure` function in `torchquantum` is used to measure the qubits. This collapses the qubits into classical states, and the measurement results give us the original message bits '10' sent by Alice with a probability of 100%."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "5aSIUmjcgO59",
+        "outputId": "84eb1859-5ff9-45b0-c1c4-dc5d15b928a3"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "[OrderedDict([('00', 0), ('01', 0), ('10', 1024), ('11', 0)])]\n"
+          ]
+        }
+      ],
+      "source": [
+        "print(tq.measure(qdev, n_shots=1024)) # Finally, Bob measures his qubits to read Alice's message"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "OPSRH4sWgpr2"
+      },
+      "source": [
+        "The `tq.measure function` in `torchquantum` is used to measure the qubits. This collapses the qubits into classical states, and the measurement results give us the original message bits '10' sent by Alice with a probability of 100%."
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "nFC9-bqHbG2I"
+      },
+      "source": [
+        "# References:\n",
+        "\n",
+        "[1] Bennett, C.H., Brassard, G., Crépeau, C., Jozsa, R., Peres, A. and Wootters, W.K., 1993. Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels. Physical review letters, 70(13), p.1895.\n",
+        "\n",
+        "[2] Bennett, C.H. and Wiesner, S.J., 1992. Communication via one-and two-particle operators on Einstein-Podolsky-Rosen states. Physical review letters, 69(20), p.2881.\n",
+        "\n",
+        "[3] [Superdense Coding - Qiskit](https://learn.qiskit.org/course/ch-algorithms/superdense-coding) \n",
+        "\n",
+        "[4] [Superdense coding - Wikipedia](https://en.wikipedia.org/wiki/Superdense_coding#:~:text=This%20protocol%20was%20first%20proposed,Zeilinger%20using%20entangled%20photon%20pairs.)"
+      ]
+    }
+  ],
+  "metadata": {
+    "colab": {
+      "provenance": []
+    },
+    "kernelspec": {
+      "display_name": "Python 3.9.1 64-bit",
+      "language": "python",
+      "name": "python3"
+    },
+    "language_info": {
+      "name": "python",
+      "version": "3.9.1"
+    },
+    "vscode": {
+      "interpreter": {
+        "hash": "f47b391bd15bf26b0169f7ceae5f0850671515af690364a96e8770f85f88a683"
+      }
+    }
+  },
+  "nbformat": 4,
+  "nbformat_minor": 0
+}
diff --git a/examples/superdense_coding/superdense_coding_torchquantum.py b/examples/superdense_coding/superdense_coding_torchquantum.py
new file mode 100644
index 00000000..4c5dd4b4
--- /dev/null
+++ b/examples/superdense_coding/superdense_coding_torchquantum.py
@@ -0,0 +1,49 @@
+import math
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from torch.nn.utils.rnn import pad_sequence
+import torchquantum as tq
+import torchquantum.functional as tqf
+import argparse
+import tqdm
+import time
+
+# Preparing the entangled state/ 2 qubit bell pair.
+def bell_pair():
+  qdev = tq.QuantumDevice(n_wires=2, bsz=1, device="cpu")
+  qdev.h(wires=0)
+  qdev.cnot(wires=[0, 1])
+  return qdev
+
+# Encoding the message
+def encode_message(qdev, qubit, msg):
+    if len(msg) != 2 or not set(msg).issubset({"0","1"}):
+        raise ValueError(f"message '{msg}' is invalid")
+    if msg[1] == "1":
+        qdev.x(wires=qubit)
+    if msg[0] == "1":
+        qdev.z(wires=qubit)
+    return qdev
+
+# Decoding the message
+def decode_message(qdev):
+    qdev.cx(wires=[0, 1])
+    qdev.h(wires=0)
+    return qdev
+
+# Putting all these functions together
+def main():
+    # Creating the entangled pair between Alice and Bob
+    qdev = bell_pair()
+    # Encoding the message at Alice's end
+    message = '10'
+    qdev = encode_message(qdev, 1, message)
+    # Decoding the original message at Bob's end
+    qdev = decode_message(qdev) 
+    # Finally, Bob measures his qubits to read Alice's message
+    print(tq.measure(qdev, n_shots=1024))
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/torchquantum/algorithms/__init__.py b/torchquantum/algorithms/__init__.py
index 18424648..9e2e1bbc 100644
--- a/torchquantum/algorithms/__init__.py
+++ b/torchquantum/algorithms/__init__.py
@@ -1,2 +1,3 @@
 from .vqe import *
-from .hamiltonian import *
\ No newline at end of file
+from .hamiltonian import *
+from .grover import *
\ No newline at end of file
diff --git a/torchquantum/algorithms/grover.py b/torchquantum/algorithms/grover.py
new file mode 100644
index 00000000..965dc7de
--- /dev/null
+++ b/torchquantum/algorithms/grover.py
@@ -0,0 +1,116 @@
+import torchquantum as tq
+
+__all__ = ["Grover"]
+
+class GroverResult(object):
+	"""Result class for Grover algorithm"""
+	def __init__(self) -> None:
+			self.iterations: int
+
+class Grover(object):
+	"""Grover's search algorithm based the paper "A fast quantum mechanical algorithm for database search" by Lov K. Grover
+	https://arxiv.org/abs/quant-ph/9605043
+	"""
+		
+	def __init__(self, oracle: tq.module.QuantumModule, iterations: int, n_wires:int) -> None:
+		"""
+		Args:
+			oracle (tq.module.QuantumModule): The oracle is a quantum module that adds a negative phase to the 
+						solution states.
+			iterations (int): The number of iterations to run the algorithm for.
+			n_wires (int): The number of qubits used in the quantum circuit.
+		"""
+		super().__init__()
+		self._oracle = oracle
+		self._iterations = iterations
+		self._n_wires = n_wires
+		
+	def initial_state_prep(self):
+		"""
+		Prepares the initial state of a quantum circuit by applying a Hadamard gate to each qubit.
+		
+		Returns:
+			a `QuantumModule` object that represents the initial state preparation circuit.
+		"""
+		ops = []
+		for i in range(self._n_wires):
+			ops.append({'name': 'hadamard', 'wires': i})
+		return tq.QuantumModule.from_op_history(ops)
+	
+	def diffusion_operator(self):
+		"""
+		Prepares the diffusion operator for the grover's circuit.
+	
+		Returns:
+			a quantum module that represents the diffusion operator for a quantum circuit.
+		"""
+		ops = []
+		hadamards = [{'name': 'hadamard', 'wires': i} for i in range(self._n_wires)]
+		flips = [{'name': 'x', 'wires': i} for i in range(self._n_wires)]
+		
+		ops += hadamards
+		ops += flips
+		
+		if self._n_wires  == 1:
+				ops += [{'name': 'z', 'wires': 0}]
+		else:
+				ops += [{'name': 'hadamard', 'wires': self._n_wires - 1}]
+				ops += [{'name': 'multicnot', 'n_wires': self._n_wires, 'wires': range(self._n_wires)}]
+				ops += [{'name': 'hadamard', 'wires': self._n_wires - 1}]
+		
+		ops += flips
+		ops += hadamards
+		
+		return tq.QuantumModule.from_op_history(ops)
+			
+					
+	def construct_grover_circuit(self, qdev: tq.QuantumDevice):
+		"""
+		Constructs a Grover's algorithm circuit with an initial state preparation, oracle,
+		and diffusion operator, and iterates through them a specified number of times.
+		
+		Args:
+			qdev (tq.QuantumDevice): tq.QuantumDevice is an object representing a quantum device or
+		simulator on which quantum circuits can be executed. 
+				
+		Returns:
+			the modified quantum device `qdev` after applying the Grover's algorithm circuit with the
+		specified number of iterations.
+		"""
+
+		self.initial_state_prep()(qdev)        
+		for _ in range(self._iterations):
+			self._oracle(qdev)
+			self.diffusion_operator()(qdev)
+		
+		return qdev
+	
+	def execute(self, qdev: tq.QuantumDevice, n_shots: int =1024):
+		"""
+		Executes a Grover search algorithm on a given quantum device and returns the result.
+		
+		Args:
+			qdev (tq.QuantumDevice): tq.QuantumDevice is an object representing a quantum device or
+		simulator on which quantum circuits can be executed. 
+			n_shots (int): The number of times the circuit is run to obtain measurement statistics.
+		Defaults to 1024
+		
+		Returns:
+			an instance of the `GroverResult` class, which contains information about the results of
+		running the Grover search algorithm on a quantum device. The `GroverResult` object includes the
+		number of iterations performed, the measured bitstring, the top measurement (i.e. the most
+		frequently measured bitstring), and the maximum probability of measuring the top measurement.
+		"""
+			
+		qdev = self.construct_grover_circuit(qdev)
+		bitstring = tq.measure(qdev, n_shots=n_shots)
+		top_measurement, max_probability = max(bitstring[0].items(), key=lambda x: x[1])
+		max_probability /= n_shots
+		
+		result = GroverResult()
+		result.iterations = self._iterations
+		result.bitstring = bitstring
+		result.top_measurement = top_measurement
+		result.max_probability = max_probability
+
+		return result
\ No newline at end of file
diff --git a/torchquantum/algorithms/hamiltonian.py b/torchquantum/algorithms/hamiltonian.py
index f9fc8855..129a7062 100644
--- a/torchquantum/algorithms/hamiltonian.py
+++ b/torchquantum/algorithms/hamiltonian.py
@@ -12,11 +12,52 @@
 __all__ = ["Hamiltonian"]
 
 class Hamiltonian(object):
-    def __init__(self, hamil_info) -> None:
+    """The Hamiltonian.
+    
+    This class provides functionality to process and initialize a Hamiltonian.
+
+    Attributes:
+        hamil_info (dict): Information about the Hamiltonian.
+        n_wires (int): Number of wires in the Hamiltonian.
+        
+    Methods:
+        __init__(hamil_info): Initializes the Hamiltonian object.
+        process_hamil_info: Process the Hamiltonian information.
+        from_file: Create a Hamiltonian from a file.
+    """
+    
+    def __init__(self, hamil_info: dict) -> None:
+        """Initialize the Hamiltonian object.
+
+        Args:
+            hamil_info (dict): Information about the Hamiltonian.
+        
+        Returns:
+            None
+            
+        Examples:
+            >>> hamil_info = {"hamil_list": [...], "n_wires": 3}
+            >>> hamiltonian = Hamiltonian(hamil_info)
+        """
+        
         self.hamil_info = self.process_hamil_info(hamil_info)
         self.n_wires = hamil_info["n_wires"]
 
-    def process_hamil_info(self, hamil_info):
+    def process_hamil_info(self, hamil_info: dict) -> dict:
+        """Process the Hamiltonian information.
+
+        Args:
+            hamil_info (dict): Information about the Hamiltonian.
+
+        Returns:
+            dict: processed Hamiltonian information
+            
+        Examples:
+            >>> hamil_info = {"hamil_list": [...], "n_wires": 3}
+            
+            >>> processed_info = self.process_hamil_info(hamil_info)
+        """
+        
         hamil_list = hamil_info["hamil_list"]
         n_wires = hamil_info["n_wires"]
         all_info = []
@@ -34,7 +75,22 @@ def process_hamil_info(self, hamil_info):
         return hamil_info
 
     @classmethod
-    def from_file(cls, file_path):
+    def from_file(cls, file_path: str) -> 'Hamiltonian':
+        """Create a Hamiltonian object from a file.
+
+        Args:
+            file_path (str): Path to the file containing Hamiltonian information.
+
+        Returns:
+            Hamiltonian: the created Hamiltonian object
+            
+        Examples:
+            >>> file_path = "hamiltonian.txt"
+            >>> hamiltonian = Hamiltonian.from_file(file_path)
+            
+            >>> hamil = Hamiltonian.from_file("hamil_file.txt")
+        """
+        
         hamil_info = parse_hamiltonian_file(file_path)
         return cls(hamil_info)
-    
\ No newline at end of file
+    
diff --git a/torchquantum/algorithms/vqe.py b/torchquantum/algorithms/vqe.py
index e01aebc2..2e953798 100644
--- a/torchquantum/algorithms/vqe.py
+++ b/torchquantum/algorithms/vqe.py
@@ -14,11 +14,35 @@
 
 
 class VQE(object):
+    """The Variational Quantum Eigensolver (VQE).
+    
+    Attributes:
+        hamil: A dictionary containing information about the Hamiltonian.
+        ansatz: An Ansatz object.
+        train_configs: A dictionary containing training configurations.
+        n_wires: An integer number of wires in the Hamiltonian.
+        n_epochs: An integer number of training epochs.
+        n_steps: An integer number of optimization steps per epoch.
+        optimizer_name: Name of the optimizer.
+        scheduler_name: Name of the learning rate scheduler.
+        lr: A float indicating learning rate.
+        device: Device to use for training.
+        
+    Methods:
+        __init__: Initialize the VQE object.
+        get_expval: Calculate the expectation value of the Hamiltonian for a given quantum device.
+        get_loss: Calculate the loss function.
+        train: Train the VQE model.
+    """
+    
     def __init__(self, hamil, ansatz, train_configs) -> None:
-        """Init function for VQE class
+        """Initialize the VQE object.
+    
         Args:
-            hamil (dict): A dictionary containing the information of the hamiltonian
-         ansatz (Ansatz): An Ansatz object
+            hamil (dict): Information about the Hamiltonian
+            ansatz (Ansatz): An Ansatz object representing the variational circuit
+            train_configs (dict): Configuration parameters for training the VQE model
+            
         """
         self.hamil = hamil
         self.ansatz = ansatz
@@ -34,6 +58,15 @@ def __init__(self, hamil, ansatz, train_configs) -> None:
         self.ansatz = self.ansatz.to(self.device)
 
     def get_expval(self, qdev):
+        """Calculate the expectation value of the Hamiltonian for a given quantum device.
+
+        Args:
+            qdev (torchquantum.QuantumDevice): Quantum device representing the state of the variational circuit
+
+        Returns:
+            float : expectation value of the Hamiltonian
+        """
+        
         hamil_list = self.hamil.hamil_info["hamil_list"]
         expval = 0
         for hamil in hamil_list:
@@ -44,6 +77,12 @@ def get_expval(self, qdev):
         return expval
 
     def get_loss(self):
+        """Calculate the loss function.
+        
+        Returns:
+            float: loss value
+        """
+        
         qdev = tq.QuantumDevice(
             n_wires=self.n_wires,
             bsz=1,
@@ -54,6 +93,12 @@ def get_loss(self):
         return expval
     
     def train(self):
+        """Train the VQE model.
+
+        Returns:
+            float: final loss value
+        """
+        
         optimizer = getattr(torch.optim, self.optimizer_name)(self.ansatz.parameters(), lr=self.lr)
         lr_scheduler = getattr(torch.optim.lr_scheduler, self.scheduler_name)(optimizer, T_max=self.n_epochs)
         loss = None
diff --git a/torchquantum/datasets/layer_regression.py b/torchquantum/datasets/layer_regression.py
index 7ea78d79..22cbef0c 100644
--- a/torchquantum/datasets/layer_regression.py
+++ b/torchquantum/datasets/layer_regression.py
@@ -6,31 +6,84 @@
 
 
 class LayerRegressionDatasetV:
+    """Layer Regression Dataset with Variational Input"""
+
     def __init__(self):
+        """Initializes a LayerRegressionDatasetV object.
+
+        Example:
+            >>> dataset = LayerRegressionDatasetV()
+        """
+
         self.data = None
         self.n_instance = 10000
         self.input = np.random.rand(5).astype(np.float) * 2 * np.pi
         self.output = np.random.rand(5).astype(np.float) * 2 - 1
 
     def __getitem__(self, index: int):
+        """Gets an instance from the dataset.
+
+        Args:
+            index (int): The index of the instance.
+
+        Returns:
+            dict: A dictionary containing the input and output.
+
+        Example:
+            >>> dataset = LayerRegressionDatasetV()
+            >>> instance = dataset[0]
+        """
+        
         instance = {"input": self.input, "output": self.output}
         return instance
 
     def __len__(self) -> int:
+        """Returns the number of instances in the dataset.
+
+        Returns:
+            int: The number of instances.
+
+        Example:
+            >>> dataset = LayerRegressionDatasetV()
+            >>> length = len(dataset)
+        """
+        
         return self.n_instance
 
 
 class LayerRegressionV(Dataset):
+    """Layer Regression Dataset with Variational Input"""
+    
     def __init__(
         self,
     ):
+        """Initializes a LayerRegressionV object.
+
+        Example:
+            >>> dataset = LayerRegressionV()
+        """
+        
         super().__init__(
             {split: LayerRegressionDatasetV() for split in ["train", "valid", "test"]}
         )
 
 
 class LayerRegressionDataset:
+    """Layer Regression Dataset
+    
+    Attributes:
+        data: The dataset.
+        n_instance (int): The number of instances.
+        output: The output data.
+    """
+    
     def __init__(self):
+        """Initializes a LayerRegressionDataset object.
+
+        Example:
+            >>> dataset = LayerRegressionDataset()
+        """
+        
         self.data = None
         self.n_instance = 10000
         mat = torch.randn((2**5, 2**5), dtype=torch.complex64)
@@ -39,17 +92,48 @@ def __init__(self):
         self.output = mat
 
     def __getitem__(self, index: int):
+        """Gets an instance from the dataset.
+
+        Args:
+            index (int): The index of the instance.
+
+        Returns:
+            dict: A dictionary containing the input and output.
+
+        Example:
+            >>> dataset = LayerRegressionDataset()
+            >>> instance = dataset[0]
+        """
+        
         instance = {"input": self.output, "output": self.output}
         return instance
 
     def __len__(self) -> int:
+        """Returns the number of instances in the dataset.
+
+        Returns:
+            int: The number of instances.
+
+        Example:
+            >>> dataset = LayerRegressionDataset()
+            >>> length = len(dataset)
+        """
+        
         return self.n_instance
 
 
 class LayerRegression(Dataset):
+    """Layer Regression Dataset"""
+    
     def __init__(
         self,
     ):
+        """Initializes a LayerRegression object.
+
+        Example:
+            >>> dataset = LayerRegression()
+        """
+        
         super().__init__(
             {split: LayerRegressionDataset() for split in ["train", "valid", "test"]}
         )
diff --git a/torchquantum/datasets/vqe.py b/torchquantum/datasets/vqe.py
index 2a003a22..eee575eb 100644
--- a/torchquantum/datasets/vqe.py
+++ b/torchquantum/datasets/vqe.py
@@ -5,16 +5,59 @@
 
 
 class VQEDataset:
+    """Dataset for VQE.
+    
+    Attributes:
+        split (str): Split of the dataset ("train", "valid", "test").
+        steps_per_epoch (int): Number of steps per epoch.
+    
+    Methods:
+        __getitem__: Get an item from the dataset
+        __len__: Get the length of the dataset
+        
+    Examples:
+        >>> dataset = VQEDataset(split='train', steps_per_epoch=100)
+        >>> instance = dataset[0]
+    """
+    
     def __init__(self, split, steps_per_epoch):
+        """Initialize the VQEDataset.
+
+        Args:
+            split (str): Split of the dataset ("train", "valid", "test")
+            steps_per_epoch (int): Number of steps per epoch
+        """
+        
         self.split = split
         self.steps_per_epoch = steps_per_epoch
 
     def __getitem__(self, index: int):
+        """Get an instance from the dataset.
+        
+        Args:
+            index (int): Index of the item
+
+        Returns:
+            dict: instance containing the input and target
+        
+        Examples:
+            >>> instance = dataset[0]
+        """
+        
         instance = {"input": -1, "target": -1}
 
         return instance
 
     def __len__(self) -> int:
+        """Get the length of the dataset.
+
+        Returns:
+            int: length of the dataset
+            
+        Examples:
+            >>> length = len(dataset)
+        """
+        
         if self.split == "train":
             return self.steps_per_epoch
         else:
@@ -22,7 +65,25 @@ def __len__(self) -> int:
 
 
 class VQE(Dataset):
+    """Dataset for the VQE.
+
+    Attributes:
+        steps_per_epoch (int): Number of steps per epoch
+
+    Methods:
+        __init__: Initialize the VQE dataset
+    
+    Examples:
+        >>> dataset = VQE(steps_per_epoch=100)
+    """
+    
     def __init__(self, steps_per_epoch):
+        """Initialize the VQE dataset.
+
+        Args:
+            steps_per_epoch (int): Number of steps per epoch
+        """
+        
         super().__init__(
             {
                 split: VQEDataset(split=split, steps_per_epoch=steps_per_epoch)
diff --git a/torchquantum/density.py b/torchquantum/density.py
index 3ab865a4..6f25b312 100644
--- a/torchquantum/density.py
+++ b/torchquantum/density.py
@@ -67,10 +67,34 @@ def __init__(self, n_wires: int, bsz: int = 1):
             self.state_list.append((1, QuantumState(n_wires)))
 
     def set_calc_by_states(self, val):
+        """Set the value of the `_calc_by_states` attribute.
+
+        This method sets the flag that determines whether calculations should be performed using individual pure states or the density matrix.
+
+        Args:
+            val (bool): The new value for the `_calc_by_states` attribute.
+
+        Returns:
+            None
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.set_calc_by_states(True)
+            >>> device.calc_by_states
+            True
+        """
+        
         self._calc_by_states = val
 
     def update_matrix_from_states(self):
-        """Update the density matrix value from all pure states"""
+        """Update the density matrix value from all pure states.
+
+        This method updates the density matrix value based on all the pure states in the state list.
+
+        Returns:
+            None
+        """
+        
         _matrix = torch.zeros(2 ** (2 * self.n_wires), dtype=C_DTYPE)
         _matrix = torch.reshape(_matrix, [2**self.n_wires, 2**self.n_wires])
         self.register_buffer("matrix", _matrix)
@@ -83,15 +107,63 @@ def update_matrix_from_states(self):
         self.register_buffer("matrix", self._matrix)
 
     def vector(self):
+        """Return the density matrix as a vector.
+
+        This method reshapes the density matrix `_matrix` into a vector representation.
+
+        Returns:
+            torch.Tensor: The density matrix as a vector.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.matrix = torch.tensor([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
+            >>> vector = device.vector()
+            >>> print(vector)
+            tensor([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        """
+        
         return torch.reshape(_matrix, [2 ** (2 * self.n_wires)])
 
     def print_2d(self, index):
-        """Print the matrix value of matrix[index]"""
+        """Print the matrix value at the given index.
+
+        This method prints the matrix value of `matrix[index]`. It reshapes the value into a 2D matrix
+        using the `torch.reshape` function and then prints it.
+
+        Args:
+            index (int): The index of the matrix value to print.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.matrix = torch.tensor([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
+            >>> device.print_2d(1)
+            tensor([[0, 0],
+                    [0, 1]])
+
+        """
+        
         _matrix = torch.reshape(self._matrix[index], [2**self.n_wires] * 2)
         print(_matrix)
 
     def trace(self, index):
-        """Return the trace of the DensityMatrix of matrix[index]"""
+        """Calculate and return the trace of the density matrix at the given index.
+
+        This method calculates the trace of the density matrix stored in `matrix[index]`
+        using the `torch.trace` function and returns the result.
+
+        Args:
+            index (int): The index of the matrix value.
+
+        Returns:
+            float: The trace of the density matrix.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.matrix = torch.tensor([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
+            >>> device.trace(0)
+            tensor(2)
+        """
+        
         return torch.trace(self._matrix[index])
 
     def positive_semidefinite(self, index):
@@ -111,6 +183,7 @@ def spectral(self, index):
 
     def tensor(self, other):
         """Return self tensor other(Notice the order)
+        
         Args:
             other (DensityMatrix: Another density matrix
         """
@@ -118,6 +191,7 @@ def tensor(self, other):
 
     def expand(self, other):
         """Return other tensor self(Notice the order)
+        
         Args:
             other (DensityMatrix: Another density matrix
         """
@@ -127,6 +201,9 @@ def clone_matrix(self, existing_matrix: torch.Tensor):
         self._matrix = existing_matrix.clone()
 
     def set_matrix(self, matrix: Union[torch.tensor, List]):
+        """
+        """
+        
         matrix = torch.tensor(matrix, dtype=C_DTYPE).to(self.matrix.device)
         bsz = matrix.shape[0]
         self.matrix = torch.reshape(
@@ -156,10 +233,12 @@ def expectation(self):
 
     def set_from_state(self, probs, states: Union[torch.Tensor, List]):
         """Get the density matrix of a mixed state.
+        
         Args:
           probs:List of probability of each state
           states:List of state.
-        For example:
+          
+        Examples:
           probs:[0.5,0.5],states:[|00>,|11>]
         Then the corresponding matrix is: 0.5|00><00|+0.5|11><11|
          0.5, 0, 0, 0
@@ -168,6 +247,7 @@ def set_from_state(self, probs, states: Union[torch.Tensor, List]):
          0 ,  0, 0, 0.5
          self._matrix[00][00]=self._matrix[11][11]=0.5
         """
+        
         for i in range(0, len(probs)):
             self.state_list
         _matrix = torch.zeros(2 ** (2 * self.n_wires), dtype=C_DTYPE)
@@ -181,9 +261,11 @@ def set_from_state(self, probs, states: Union[torch.Tensor, List]):
 
     def _add(self, other):
         """Return self + other
+        
         Args:
             other (complex): a complex number.
         """
+        
         if not isinstance(other, DensityMatrix):
             other = DensityMatrix(other)
         if not self._matrix.shape == other._matrix.shape:
@@ -194,9 +276,11 @@ def _add(self, other):
 
     def _multiply(self, other):
         """Return other * self.
+        
         Args:
             other (complex): a complex number.
         """
+        
         ret = copy.copy(self)
         ret._matrix = other * self._matrix
         return ret
@@ -206,7 +290,8 @@ def purity(self):
         return torch.trace(torch.matmul(self._matrix, self._matrix))
 
     def partial_trace(self, dims: List[int]):
-        """Calculate the partial trace of given sub-dimension, return a new density_matrix
+        """Calculate the partial trace of given sub-dimension, return a new density_matrix.
+        
         Args:
             dims:The list of sub-dimension
             For example, If we have 3 qubit, the matrix shape is (8,8),
@@ -214,6 +299,7 @@ def partial_trace(self, dims: List[int]):
             First, the matrix should be reshped to (2,2,2,2,2,2)
             then we call  np.einsum('ijiiqi->jq', reshaped_dm)
         """
+        
         return False
 
     @property
@@ -229,6 +315,29 @@ def hadamard(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a Hadamard gate on the specified wires.
+
+        This method applies a Hadamard gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Hadamard gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.hadamard(wires=[0, 1], inverse=False)
+        """
         dfunc.hadamard(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def shadamard(
@@ -237,6 +346,29 @@ def shadamard(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a SHadamard gate (square root of Hadamard gate) on the specified wires.
+
+        This method applies a SHadamard gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the SHadamard gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.shadamard(wires=[0, 1], inverse=False)
+        """
         dfunc.shadamard(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def paulix(
@@ -245,6 +377,30 @@ def paulix(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a Pauli-X gate (also known as the NOT gate) on the specified wires.
+
+        This method applies a Pauli-X gate (NOT gate) on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Pauli-X gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.paulix(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.paulix(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def pauliy(
@@ -253,6 +409,30 @@ def pauliy(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a Pauli-Y gate on the specified wires.
+
+        This method applies a Pauli-Y gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Pauli-Y gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.pauliy(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.pauliy(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def pauliz(
@@ -261,6 +441,29 @@ def pauliz(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a Pauli-Z gate on the specified wires.
+
+        This method applies a Pauli-Z gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Pauli-Z gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.pauliz(wires=[0, 1], inverse=False)
+        """
         dfunc.pauliz(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def i(
@@ -269,6 +472,30 @@ def i(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply an identity gate on the specified wires.
+
+        This method applies an identity gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the identity gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.i(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.i(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def s(
@@ -277,6 +504,30 @@ def s(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply an S gate on the specified wires.
+
+        This method applies an S gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the S gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.s(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.s(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def t(
@@ -285,6 +536,29 @@ def t(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a T gate on the specified wires.
+
+        This method applies a T gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the T gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires. Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.t(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.t(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def sx(
@@ -293,6 +567,30 @@ def sx(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a SX gate (square root of Pauli-X gate) on the specified wires.
+
+        This method applies a SX gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the SX gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.sx(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.sx(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def cnot(
@@ -301,6 +599,30 @@ def cnot(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a controlled-NOT (CNOT) gate on the specified wires.
+
+        This method applies a controlled-NOT gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the CNOT gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.cnot(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.cnot(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def cz(
@@ -309,6 +631,30 @@ def cz(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a controlled-Z (CZ) gate on the specified wires.
+
+        This method applies a controlled-Z gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the CZ gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.cz(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.cz(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def cy(
@@ -317,6 +663,30 @@ def cy(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a controlled-Y (CY) gate on the specified wires.
+
+        This method applies a controlled-Y gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the CY gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.cy(wires=[0, 1], inverse=False)
+        """
+        
         dfunc.cy(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def swap(
@@ -325,6 +695,30 @@ def swap(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a swap gate on the specified wires.
+
+        This method applies a swap gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the swap gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.swap(wires=[0, 1, 2], inverse=False)
+        """
+        
         dfunc.swap(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def sswap(
@@ -333,6 +727,30 @@ def sswap(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+         """Apply a symmetric swap gate on the specified wires.
+
+        This method applies a symmetric swap gate on the specified wires of the quantum device.
+        The gate is applied to all the wires if the inverse flag is set to False.
+        If the inverse flag is set to True, the gate is applied to all the wires except the first one.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the symmetric swap gate.
+            inverse (bool, optional): If True, apply the gate to all the wires except the first one.
+                If False, apply the gate to all the wires.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.sswap(wires=[0, 1, 2], inverse=False)
+        """
+            
         dfunc.sswap(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def cswap(
@@ -341,6 +759,30 @@ def cswap(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a controlled swap (Fredkin) gate on the specified wires.
+
+        This method applies a controlled swap (Fredkin) gate on the specified wires of the quantum device.
+        The gate is applied to all the wires except the control wire if the inverse flag is set to True.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the controlled swap gate.
+            inverse (bool, optional): If True, apply the gate as a swap gate
+                (i.e., swap the target wires if the control wire is ON).
+                If False, apply the gate as a controlled swap gate (Fredkin gate).
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.cswap(wires=[0, 1, 2], inverse=False)
+        """
+        
         dfunc.cswap(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def toffoli(
@@ -349,6 +791,30 @@ def toffoli(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        """Apply a Toffoli (CCNOT) gate on the specified wires.
+
+        This method applies a Toffoli (CCNOT) gate on the specified wires of the quantum device.
+        The gate is applied to all the wires except the control wires if the inverse flag is set to True.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Toffoli gate.
+            inverse (bool, optional): If True, apply the gate as an X gate
+                (i.e., flip the target wire if both control wires are ON).
+                If False, apply the gate as a Toffoli gate.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.toffoli(wires=[0, 1, 2], inverse=False)
+        """
+        
         dfunc.toffoli(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def multicnot(
@@ -358,6 +824,30 @@ def multicnot(
         comp_method: str = "bmm",
     ):
 
+        """Apply a multi-qubit controlled-NOT (CNOT) gate on the specified wires.
+
+        This method applies a multi-qubit controlled-NOT (CNOT) gate on the specified wires of the quantum device.
+        The gate is applied to all the wires except the control wire(s) if the inverse flag is set to True.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the CNOT gate.
+            inverse (bool, optional): If True, apply the gate as a multi-qubit X gate
+                (i.e., flip all target wires except the control wire(s)).
+                If False, apply the gate as a multi-qubit CNOT gate.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.multicnot(wires=[0, 1, 2], inverse=False)
+        """
+        
         dfunc.multicnot(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def multixcnot(
@@ -367,6 +857,30 @@ def multixcnot(
         comp_method: str = "bmm",
     ):
 
+        """Apply a multi-qubit X gate or CNOT gate on the specified wires.
+
+        This method applies a multi-qubit X gate or CNOT gate on the specified wires of the quantum device.
+        The gate is applied to all the wires except the control wire(s) if the inverse flag is set to True.
+        The computation method for applying the gate can be controlled using the comp_method parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the X or CNOT gate.
+            inverse (bool, optional): If True, apply the gate as a multi-qubit X gate
+                (i.e., flip all target wires except the control wire(s)).
+                If False, apply the gate as a multi-qubit CNOT gate.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.multixcnot(wires=[0, 1, 2], inverse=False)
+        """
+        
         dfunc.multixcnot(self, wires=wires, inverse=inverse, comp_method=comp_method)
 
     def rx(
@@ -377,6 +891,32 @@ def rx(
         comp_method: str = "bmm",
     ):
 
+        """Apply a single-qubit Rx gate on the specified wires.
+
+        This method applies a single-qubit Rx gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` tensor. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Rx gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the Rx gate. It can be a tensor, numpy array, or a list of floats or ints.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=1)
+            >>> params = torch.tensor(0.5)
+            >>> device.rx(wires=0, params=params)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -394,6 +934,31 @@ def ry(
         comp_method: str = "bmm",
     ):
 
+        """Apply a single-qubit Ry gate on the specified wires.
+
+        This method applies a single-qubit Ry gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` tensor. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Ry gate.
+            params (torch.Tensor): The parameters of the Ry gate. It should be a tensor.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=1)
+            >>> params = torch.tensor(0.5)
+            >>> device.ry(wires=0, params=params)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -411,6 +976,31 @@ def rz(
         comp_method: str = "bmm",
     ):
 
+        """Apply a single-qubit Rz gate on the specified wires.
+
+        This method applies a single-qubit Rz gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` tensor. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the Rz gate.
+            params (torch.Tensor): The parameters of the Rz gate. It should be a tensor.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=1)
+            >>> params = torch.tensor(0.5)
+            >>> device.rz(wires=0, params=params)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -428,6 +1018,32 @@ def rxx(
         comp_method: str = "bmm",
     ):
 
+        """Apply a rotation XX gate on the specified wires.
+
+        This method applies a rotation XX gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the rotation XX gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the rotation XX gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.rxx(wires=[0, 1], params=0.1)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -445,6 +1061,32 @@ def ryy(
         comp_method: str = "bmm",
     ):
 
+        """Apply a rotation YY gate on the specified wires.
+
+        This method applies a rotation YY gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the rotation YY gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the rotation YY gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.rzz(wires=[0, 1], params=0.1)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -462,6 +1104,32 @@ def rzz(
         comp_method: str = "bmm",
     ):
 
+        """Apply a rotation ZZ gate on the specified wires.
+
+        This method applies a controlled ZZ gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the rotation ZZ gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the rotation ZZ gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.rzz(wires=[0, 1], params=0.3)
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -479,6 +1147,32 @@ def rzx(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled Rz gate on the specified wires.
+
+        This method applies a controlled Rz gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the controlled Rz gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled Rz gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.rzx(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -496,6 +1190,32 @@ def phaseshift(
         comp_method: str = "bmm",
     ):
 
+        """Apply a phase shift gate on the specified wires.
+
+        This method applies a phase shift gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the phase shift gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the phase shift gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.phaseshift(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -513,6 +1233,32 @@ def rot(
         comp_method: str = "bmm",
     ):
 
+        """Apply a rotation gate on the specified wires.
+
+        This method applies a rotation gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The wires on which to apply the rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.rot(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -533,6 +1279,32 @@ def multirz(
         comp_method: str = "bmm",
     ):
 
+        """Apply a multi-controlled Z-rotation gate on the specified control wires.
+
+        This method applies a multi-controlled Z-rotation gate on the specified control wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control wires on which to apply the multi-controlled Z-rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the multi-controlled Z-rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.multirz(wires=[0, 1], params=[0.1, 0.2])
+        """
+
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -550,6 +1322,32 @@ def crx(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled X-rotation gate on the specified control and target wires.
+
+        This method applies a controlled X-rotation gate on the specified control and target wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the controlled X-rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled X-rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.crx(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -567,6 +1365,32 @@ def cry(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled Y-rotation gate on the specified control and target wires.
+
+        This method applies a controlled Y-rotation gate on the specified control and target wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the controlled Y-rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled Y-rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.cry(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -584,6 +1408,32 @@ def crz(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled phase rotation gate on the specified control and target wires.
+
+        This method applies a controlled phase rotation gate on the specified control and target wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the controlled phase rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled phase rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.crz(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -601,6 +1451,32 @@ def crot(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled-rotation gate on the specified control and target wires.
+
+        This method applies a controlled-rotation gate on the specified control and target wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The control and target wires on which to apply the controlled-rotation gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled-rotation gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.crot(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -621,6 +1497,32 @@ def u1(
         comp_method: str = "bmm",
     ):
 
+        """Apply a u1 gate on the specified wires.
+
+        This method applies a u1 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the u1 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the u1 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.u1(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -638,6 +1540,32 @@ def u2(
         comp_method: str = "bmm",
     ):
 
+        """Apply a u2 gate on the specified wires.
+
+        This method applies a u2 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the u2 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the u2 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.u2(wires=[0, 1], params=[0.1, 0.2])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -658,6 +1586,32 @@ def u3(
         comp_method: str = "bmm",
     ):
 
+        """Apply a u3 gate on the specified wires.
+
+        This method applies a u3 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the u3 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the u3 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.u3(wires=[0, 1], params=[0.1, 0.2, 0.3])
+
+        """
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -678,6 +1632,32 @@ def cu1(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled-u1 gate on the specified wires.
+
+        This method applies a controlled-u1 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the controlled-u1 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled-u1 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.cu1(wires=[0, 1], params=[0.3, 0.5])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -698,6 +1678,32 @@ def cu2(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled-u2 gate on the specified wires.
+
+        This method applies a controlled-u2 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the controlled-u2 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled-u2 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=2)
+            >>> device.cu2(wires=[0, 1], params=[0.3, 0.5])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -718,6 +1724,32 @@ def cu3(
         comp_method: str = "bmm",
     ):
 
+        """Apply a controlled-u3 gate on the specified wires.
+
+        This method applies a controlled-u3 gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the controlled-u3 gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the controlled-u3 gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.cu3(wires=[0, 1], params=[0.5, 0.5, 0.5])
+
+        """
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
@@ -738,6 +1770,32 @@ def qubitunitary(
         comp_method: str = "bmm",
     ):
 
+        """Apply a unitary gate on the specified wires.
+
+        This method applies a unitary gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.qubitunitary(wires=[0, 1], params=[0.5, 0.5])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.tensor(params, dtype=C_DTYPE)
         else:
@@ -755,6 +1813,33 @@ def qubitunitaryfast(
         comp_method: str = "bmm",
     ):
 
+        """Apply a unitary gate on the specified wires (fast method).
+
+        This method applies a unitary gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` flag to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter. This method uses a fast implementation
+        for applying the gate.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.qubitunitaryfast(wires=[0, 1], params=[0.5, 0.5])
+        """
+        
         if isinstance(params, Iterable):
             params = torch.tensor(params, dtype=C_DTYPE)
         else:
@@ -772,6 +1857,32 @@ def qubitunitarystrict(
         comp_method: str = "bmm",
     ):
 
+        """Apply a unitary gate on the specified wires.
+
+        This method applies a unitary gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` parameter to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
+
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.qubitunitarystrict(wires=[0, 1], params=[0.5, 0.5])
+
+        """
         if isinstance(params, Iterable):
             params = torch.tensor(params, dtype=C_DTYPE)
         else:
@@ -788,7 +1899,33 @@ def single_excitation(
         inverse: bool = False,
         comp_method: str = "bmm",
     ):
+        
+        """Apply a single excitation gate on the specified wires.
+        
+        This method applies a single excitation gate on the specified wires of the quantum device.
+        The gate is parametrized by the given `params` values. The gate can be applied in the inverse
+        direction by setting the `inverse` parameter to True. The computation method for applying the gate
+        can be controlled using the `comp_method` parameter.
 
+        Args:
+            wires (Union[List[int], int]): The target wires on which to apply the gate.
+            params (Union[torch.Tensor, np.ndarray, List[float], List[int], int, float]):
+                The parameters of the gate. It can be a tensor or array-like object.
+                If a single value is provided, it will be broadcasted to all parameters.
+            inverse (bool, optional): If True, apply the gate in the inverse direction.
+                Defaults to False.
+            comp_method (str, optional): The computation method for applying the gate.
+                Supported options are "bmm" (batch matrix multiplication) and "einsum" (Einstein summation).
+                Defaults to "bmm".
+        
+        Returns:
+            None.
+
+        Examples:
+            >>> device = QuantumDevice(n_wires=3)
+            >>> device.single_excitation(wires=[0, 2], params=[0.1, 0.2])
+        """
+    
         if isinstance(params, Iterable):
             params = torch.Tensor(params)
         else:
diff --git a/torchquantum/densityfunc.py b/torchquantum/densityfunc.py
index 2d1d3a85..ac3090ea 100644
--- a/torchquantum/densityfunc.py
+++ b/torchquantum/densityfunc.py
@@ -90,6 +90,7 @@ def apply_unitary_density_einsum(density, mat, wires):
     Returns:
         torch.Tensor: The new statevector.
     """
+    
     device_wires = wires
     n_qubit = int((density.dim() - 1) / 2)
 
@@ -210,6 +211,7 @@ def apply_unitary_density_bmm(density, mat, wires):
     Returns:
         torch.Tensor: The new statevector.
     """
+    
     device_wires = wires
     n_qubit = int((density.dim() - 1) / 2)
 
@@ -299,8 +301,8 @@ def gate_wrapper(
 
     Returns:
         None.
-
     """
+    
     if params is not None:
         if not isinstance(params, torch.Tensor):
             # this is for qubitunitary gate
@@ -360,8 +362,28 @@ def gate_wrapper(
             q_device.states = apply_unitary_density_bmm(state, matrix, wires)
 
 
-def reset(q_device: tq.QuantumDevice, wires, inverse=False):
-    # reset the target qubits to 0, non-unitary operation
+def reset(q_device: tq.QuantumDevice, wires, inverse=False) -> None:
+    """Reset the target qubits to the state 0. It is a non-unitary operation.
+
+    Args:
+        q_device (tq.QuantumDevice): The quantum device.
+        wires (int or list): The target wire(s) to reset.
+        inverse (bool, optional): If True, performs an inverse reset operation. 
+            Defaults to False.
+
+    Returns:
+        None.
+
+    Examples:
+        >>> device = tq.QuantumDevice(n_wires=3)
+        >>> reset(device, wires=1)
+        >>> print(device.states)
+        [1., 0., 1., 0., 0., 0., 0., 0.]
+
+        >>> reset(device, wires=[0, 2])
+        >>> print(device.states)
+        [0., 0., 0., 0., 0., 0., 0., 0.]
+    """
     state = q_device.states
 
     wires = [wires] if isinstance(wires, int) else wires
@@ -394,8 +416,8 @@ def rx_matrix(params: torch.Tensor) -> torch.Tensor:
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     """
     Seems to be a pytorch bug. Have to explicitly cast the theta to a
@@ -406,8 +428,8 @@ def rx_matrix(params: torch.Tensor) -> torch.Tensor:
     (input_is_complex == grad_is_complex) to be true, but got false.
     (Could this error message be improved?
     If so, please report an enhancement request to PyTorch.)
-
     """
+    
     co = torch.cos(theta / 2)
     jsi = 1j * torch.sin(-theta / 2)
 
@@ -424,8 +446,8 @@ def ry_matrix(params: torch.Tensor) -> torch.Tensor:
 
     Returns:
         The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
 
     co = torch.cos(theta / 2)
@@ -444,8 +466,8 @@ def rz_matrix(params: torch.Tensor) -> torch.Tensor:
 
     Returns:
         The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     exp = torch.exp(-0.5j * theta)
 
@@ -461,6 +483,23 @@ def rz_matrix(params: torch.Tensor) -> torch.Tensor:
 
 
 def phaseshift_matrix(params):
+    """Compute the phase shift matrix.
+
+    Args:
+        params (torch.Tensor): Input parameters.
+
+    Returns:
+        torch.Tensor: The phase shift matrix.
+
+    Examples:
+        >>> params = torch.tensor([0.5])
+        >>> matrix = phaseshift_matrix(params)
+        >>> print(matrix)
+
+        >>> params = torch.tensor([1.0, 2.0, 3.0])
+        >>> matrix = phaseshift_matrix(params)
+        >>> print(matrix)
+    """
     phi = params.type(C_DTYPE)
     exp = torch.exp(1j * phi)
 
@@ -487,8 +526,8 @@ def rot_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
     theta = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
     omega = params[:, 2].unsqueeze(dim=-1).type(C_DTYPE)
@@ -525,8 +564,8 @@ def multirz_eigvals(params, n_wires):
 
     Returns:
         torch.Tensor: The computed eigenvalues.
-
     """
+    
     theta = params.type(C_DTYPE)
     return torch.exp(
         -1j * theta / 2 * torch.tensor(pauli_eigs(n_wires)).to(params.device)
@@ -541,8 +580,8 @@ def multirz_matrix(params, n_wires):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     # torch diagonal not available for complex number
     eigvals = multirz_eigvals(params, n_wires)
     dia = diag(eigvals)
@@ -557,7 +596,6 @@ def rxx_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
 
     theta = params.type(C_DTYPE)
@@ -595,8 +633,8 @@ def ryy_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     jsi = 1j * torch.sin(theta / 2)
@@ -632,8 +670,8 @@ def rzz_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     exp = torch.exp(-0.5j * theta)
     conj_exp = torch.conj(exp)
@@ -664,8 +702,8 @@ def rzx_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     jsi = 1j * torch.sin(theta / 2)
@@ -703,8 +741,8 @@ def crx_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     jsi = 1j * torch.sin(-theta / 2)
@@ -734,8 +772,8 @@ def cry_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     si = torch.sin(theta / 2)
@@ -765,8 +803,8 @@ def crz_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     exp = torch.exp(-0.5j * theta)
 
@@ -793,8 +831,8 @@ def crot_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params[:, 0].type(C_DTYPE)
     theta = params[:, 1].type(C_DTYPE)
     omega = params[:, 2].type(C_DTYPE)
@@ -828,8 +866,8 @@ def u1_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params.type(C_DTYPE)
     exp = torch.exp(1j * phi)
 
@@ -856,8 +894,8 @@ def cu1_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params.type(C_DTYPE)
     exp = torch.exp(1j * phi)
 
@@ -884,8 +922,8 @@ def u2_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
     lam = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
 
@@ -909,8 +947,8 @@ def cu2_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     phi = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
     lam = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
 
@@ -939,8 +977,8 @@ def u3_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
     phi = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
     lam = params[:, 2].unsqueeze(dim=-1).type(C_DTYPE)
@@ -967,8 +1005,8 @@ def cu3_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
     phi = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
     lam = params[:, 2].unsqueeze(dim=-1).type(C_DTYPE)
@@ -1002,8 +1040,11 @@ def qubitunitary_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
+        
+    Raises:
+        AssertionError: If Operator is other than square matrix
     """
+    
     matrix = params.squeeze(0)
     try:
         assert matrix.shape[-1] == matrix.shape[-2]
@@ -1042,8 +1083,8 @@ def qubitunitaryfast_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     return params.squeeze(0)
 
 
@@ -1056,8 +1097,8 @@ def qubitunitarystrict_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     params.squeeze(0)
     mat = params
     U, Sigma, V = torch.svd(mat)
@@ -1072,8 +1113,8 @@ def multicnot_matrix(n_wires):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     mat = torch.eye(2**n_wires, dtype=C_DTYPE)
     mat[-1][-1] = 0
     mat[-2][-2] = 0
@@ -1091,8 +1132,8 @@ def multixcnot_matrix(n_wires):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     # when all control qubits are zero, then the target qubit will flip
     mat = torch.eye(2**n_wires, dtype=C_DTYPE)
     mat[0][0] = 0
@@ -1111,8 +1152,8 @@ def single_excitation_matrix(params):
 
     Returns:
         torch.Tensor: The computed unitary matrix.
-
     """
+    
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     si = torch.sin(theta / 2)
@@ -1258,8 +1299,8 @@ def hadamard(
 
     Returns:
         None.
-
     """
+    
     name = "hadamard"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1305,8 +1346,8 @@ def shadamard(
 
     Returns:
         None.
-
     """
+    
     name = "shadamard"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1352,8 +1393,8 @@ def paulix(
 
     Returns:
         None.
-
     """
+    
     name = "paulix"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1399,8 +1440,8 @@ def pauliy(
 
     Returns:
         None.
-
     """
+    
     name = "pauliy"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1446,8 +1487,8 @@ def pauliz(
 
     Returns:
         None.
-
     """
+    
     name = "pauliz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1493,8 +1534,8 @@ def i(
 
     Returns:
         None.
-
     """
+    
     name = "i"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1540,8 +1581,8 @@ def s(
 
     Returns:
         None.
-
     """
+    
     name = "s"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1587,8 +1628,8 @@ def t(
 
     Returns:
         None.
-
     """
+    
     name = "t"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1634,8 +1675,8 @@ def sx(
 
     Returns:
         None.
-
     """
+    
     name = "sx"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1681,8 +1722,8 @@ def cnot(
 
     Returns:
         None.
-
     """
+    
     name = "cnot"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1728,8 +1769,8 @@ def cz(
 
     Returns:
         None.
-
     """
+    
     name = "cz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1775,8 +1816,8 @@ def cy(
 
     Returns:
         None.
-
     """
+    
     name = "cy"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1822,8 +1863,8 @@ def rx(
 
     Returns:
         None.
-
     """
+    
     name = "rx"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1869,8 +1910,8 @@ def ry(
 
     Returns:
         None.
-
     """
+    
     name = "ry"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1916,8 +1957,8 @@ def rz(
 
     Returns:
         None.
-
     """
+    
     name = "rz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -1963,8 +2004,8 @@ def rxx(
 
     Returns:
         None.
-
     """
+    
     name = "rxx"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2010,8 +2051,8 @@ def ryy(
 
     Returns:
         None.
-
     """
+    
     name = "ryy"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2057,8 +2098,8 @@ def rzz(
 
     Returns:
         None.
-
     """
+    
     name = "rzz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2104,8 +2145,8 @@ def rzx(
 
     Returns:
         None.
-
     """
+    
     name = "rzx"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2151,8 +2192,8 @@ def swap(
 
     Returns:
         None.
-
     """
+    
     name = "swap"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2198,8 +2239,8 @@ def sswap(
 
     Returns:
         None.
-
     """
+    
     name = "sswap"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2245,8 +2286,8 @@ def cswap(
 
     Returns:
         None.
-
     """
+    
     name = "cswap"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2292,8 +2333,8 @@ def toffoli(
 
     Returns:
         None.
-
     """
+    
     name = "toffoli"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2339,8 +2380,8 @@ def phaseshift(
 
     Returns:
         None.
-
     """
+    
     name = "phaseshift"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2386,8 +2427,8 @@ def rot(
 
     Returns:
         None.
-
     """
+    
     name = "rot"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2433,8 +2474,8 @@ def multirz(
 
     Returns:
         None.
-
     """
+    
     name = "multirz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2480,8 +2521,8 @@ def crx(
 
     Returns:
         None.
-
     """
+    
     name = "crx"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2527,8 +2568,8 @@ def cry(
 
     Returns:
         None.
-
     """
+    
     name = "cry"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2574,8 +2615,8 @@ def crz(
 
     Returns:
         None.
-
     """
+    
     name = "crz"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2621,8 +2662,8 @@ def crot(
 
     Returns:
         None.
-
     """
+    
     name = "crot"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2668,8 +2709,8 @@ def u1(
 
     Returns:
         None.
-
     """
+    
     name = "u1"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2715,8 +2756,8 @@ def u2(
 
     Returns:
         None.
-
     """
+    
     name = "u2"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2762,8 +2803,8 @@ def u3(
 
     Returns:
         None.
-
     """
+    
     name = "u3"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2809,8 +2850,8 @@ def cu1(
 
     Returns:
         None.
-
     """
+    
     name = "cu1"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2856,8 +2897,8 @@ def cu2(
 
     Returns:
         None.
-
     """
+    
     name = "cu2"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2903,8 +2944,8 @@ def cu3(
 
     Returns:
         None.
-
     """
+    
     name = "cu3"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2950,8 +2991,8 @@ def qubitunitary(
 
     Returns:
         None.
-
     """
+    
     name = "qubitunitary"
     mat = mat_dict[name]
     gate_wrapper(
@@ -2997,8 +3038,8 @@ def qubitunitaryfast(
 
     Returns:
         None.
-
     """
+    
     name = "qubitunitaryfast"
     mat = mat_dict[name]
     gate_wrapper(
@@ -3044,8 +3085,8 @@ def qubitunitarystrict(
 
     Returns:
         None.
-
     """
+    
     name = "qubitunitarystrict"
     mat = mat_dict[name]
     gate_wrapper(
@@ -3091,8 +3132,8 @@ def multicnot(
 
     Returns:
         None.
-
     """
+    
     name = "multicnot"
     mat = mat_dict[name]
     gate_wrapper(
@@ -3138,8 +3179,8 @@ def multixcnot(
 
     Returns:
         None.
-
     """
+    
     name = "multixcnot"
     mat = mat_dict[name]
     gate_wrapper(
@@ -3185,8 +3226,8 @@ def single_excitation(
 
     Returns:
         None.
-
     """
+    
     name = "single_excitation"
     mat = mat_dict[name]
     gate_wrapper(
diff --git a/torchquantum/devices.py b/torchquantum/devices.py
index bc34dcd0..f0c62932 100644
--- a/torchquantum/devices.py
+++ b/torchquantum/devices.py
@@ -50,6 +50,7 @@ def __init__(
         self.op_history = []
 
     def reset_op_history(self):
+        """Resets the all Operation of the quantum device"""
         self.op_history = []
 
     def clone_states(self, existing_states: torch.Tensor):
@@ -62,6 +63,7 @@ def set_states(self, states: torch.Tensor):
         self.states = torch.reshape(states, [bsz] + [2] * self.n_wires)
 
     def reset_states(self, bsz: int):
+        """Reset the States of the quantum device"""
         repeat_times = [bsz] + [1] * len(self.state.shape)
         self.states = self.state.repeat(*repeat_times).to(self.state.device)
 
diff --git a/torchquantum/encoding.py b/torchquantum/encoding.py
index a1758350..33db5064 100644
--- a/torchquantum/encoding.py
+++ b/torchquantum/encoding.py
@@ -9,6 +9,12 @@
 
 
 class Encoder(tq.QuantumModule):
+    """Encodes the given classic state to quantum state.
+
+    Methods:
+        - forward(qdev: tq.QuantumDevice, x): Performs the encoding using a quantum device.
+
+    """
     def __init__(self):
         super().__init__()
         pass
@@ -101,12 +107,25 @@ def to_qiskit(self, n_wires, x):
 
 
 class PhaseEncoder(Encoder, metaclass=ABCMeta):
+    """PhaseEncoder is a subclass of Encoder and represents a phase encoder.
+    It applies a specified quantum function to encode input data using a quantum device."""
     def __init__(self, func):
         super().__init__()
         self.func = func
 
     @tq.static_support
     def forward(self, qdev: tq.QuantumDevice, x):
+        """
+                Performs the encoding using a quantum device.
+
+                Args:
+                    qdev (tq.QuantumDevice): The quantum device to be used for encoding.
+                    x (torch.Tensor): The input data to be encoded.
+
+                Returns:
+                    torch.Tensor: The encoded data.
+
+                """
         for k in range(qdev.n_wires):
             self.func(
                 qdev,
@@ -118,6 +137,8 @@ def forward(self, qdev: tq.QuantumDevice, x):
 
 
 class MultiPhaseEncoder(Encoder, metaclass=ABCMeta):
+    """PhaseEncoder is a subclass of Encoder and represents a phase encoder.
+    It applies a specified quantum function to encode input data using a quantum device."""
     def __init__(self, funcs, wires=None):
         super().__init__()
         self.funcs = funcs if isinstance(funcs, Iterable) else [funcs]
@@ -125,6 +146,17 @@ def __init__(self, funcs, wires=None):
 
     @tq.static_support
     def forward(self, qdev: tq.QuantumDevice, x):
+        """
+                Performs the encoding using a quantum device.
+
+                Args:
+                    qdev (tq.QuantumDevice): The quantum device to be used for encoding.
+                    x (torch.Tensor): The input data to be encoded.
+
+                Returns:
+                    torch.Tensor: The encoded data.
+
+                """
         if self.wires is None:
             self.wires = list(range(qdev.n_wires)) * (len(self.funcs) // qdev.n_wires)
 
@@ -150,10 +182,22 @@ def forward(self, qdev: tq.QuantumDevice, x):
 
 
 class StateEncoder(Encoder, metaclass=ABCMeta):
+    """StateEncoder is a subclass of Encoder and represents a state encoder.
+    It encodes the input data into the state vector of a quantum device."""
     def __init__(self):
         super().__init__()
 
     def forward(self, qdev: tq.QuantumDevice, x):
+        """
+            Performs the encoding by preparing the state vector of the quantum device.
+
+                Args:
+                    qdev (tq.QuantumDevice): The quantum device to be used for encoding.
+                    x (torch.Tensor): The input data to be encoded.
+                Returns:
+                    torch.Tensor: The encoded data.
+
+                """
         # encoder the x to the statevector of the quantum device
 
         # normalize the input
@@ -173,6 +217,8 @@ def forward(self, qdev: tq.QuantumDevice, x):
 
 
 class MagnitudeEncoder(Encoder, metaclass=ABCMeta):
+    """MagnitudeEncoder is a subclass of Encoder and represents a magnitude encoder.
+    It encodes the input data by considering the magnitudes of the elements."""
     def __init__(self):
         super().__init__()
 
diff --git a/torchquantum/functional.py b/torchquantum/functional.py
index 94c77074..b947a77e 100644
--- a/torchquantum/functional.py
+++ b/torchquantum/functional.py
@@ -83,6 +83,22 @@
     "reset",
     "ecr",
     "echoedcrossresonance",
+    "qft",
+    "sdg",
+    "iswap",
+    "cs",
+    "csdg",
+    "csx",
+    "chadamard",
+    "ccz",
+    "dcx",
+    "xxminyy",
+    "xxplusyy",
+    "c3x",
+    "tdg",
+    "sxdg",
+    "ch",
+    "r",
 ]
 
 
@@ -298,7 +314,7 @@ def gate_wrapper(
                 "qubitunitarystrict",
             ]:
                 matrix = mat(params)
-            elif name in ["multicnot", "multixcnot"]:
+            elif name in ["multicnot", "multixcnot", "qft"]:
                 # this is for gates that can be applied to arbitrary numbers of
                 # qubits but no params, such as multicnot
                 matrix = mat(n_wires)
@@ -427,6 +443,15 @@ def rz_matrix(params: torch.Tensor) -> torch.Tensor:
 
 
 def phaseshift_matrix(params):
+    """Compute unitary matrix for phaseshift gate.
+
+        Args:
+            params (torch.Tensor): The rotation angle.
+
+        Returns:
+            torch.Tensor: The computed unitary matrix.
+
+        """
     phi = params.type(C_DTYPE)
     exp = torch.exp(1j * phi)
 
@@ -482,6 +507,121 @@ def rot_matrix(params):
         dim=-2,
     ).squeeze(0)
 
+def xxminyy_matrix(params):
+    """Compute unitary matrix for XXminusYY gate.
+
+    Args:
+        params (torch.Tensor): The rotation angle. (Theta,Beta)
+
+    Returns:
+        torch.Tensor: The computed unitary matrix.
+
+    """
+    theta = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
+    beta = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
+
+    co = torch.cos(theta / 2)
+    si = torch.sin(theta / 2)
+
+    return torch.stack(
+        [
+            torch.cat(
+                [
+                    co,
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    (-1j*si*torch.exp(-1j*beta)),
+                ],
+                dim=-1,
+            ),
+            torch.cat(
+                [
+                    torch.tensor([[0]]),
+                    torch.tensor([[1]]),
+                    torch.tensor([[0]]),
+                   torch.tensor([[0]]),
+                ],
+                dim=-1,
+            ),
+             torch.cat(
+                [
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    torch.tensor([[1]]),
+                    torch.tensor([[0]]),
+                ],
+                dim=-1,
+            ),
+             torch.cat(
+                [
+                    (-1j*si*torch.exp(1j*beta)),
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    co,
+                ],
+                dim=-1,
+            ),
+        ],
+        dim=-2,
+    ).squeeze(0)
+
+def xxplusyy_matrix(params):
+    """Compute unitary matrix for XXplusYY gate.
+
+    Args:
+        params (torch.Tensor): The rotation angle. (Theta,Beta)
+
+    Returns:
+        torch.Tensor: The computed unitary matrix.
+
+    """
+    theta = params[:, 0].unsqueeze(dim=-1).type(C_DTYPE)
+    beta = params[:, 1].unsqueeze(dim=-1).type(C_DTYPE)
+
+    co = torch.cos(theta / 2)
+    si = torch.sin(theta / 2)
+
+    return torch.stack(
+        [
+            torch.cat(
+                [
+                    torch.tensor([[1]]),
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                ],
+                dim=-1,
+            ),
+            torch.cat(
+                [
+                    torch.tensor([[0]]),
+                    co,
+                    (-1j*si*torch.exp(-1j*beta)),
+                   torch.tensor([[0]]),
+                ],
+                dim=-1,
+            ),
+             torch.cat(
+                [
+                    torch.tensor([[0]]),
+                    (-1j*si*torch.exp(1j*beta)),
+                    co,
+                    torch.tensor([[0]]),
+                ],
+                dim=-1,
+            ),
+             torch.cat(
+                [
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    torch.tensor([[0]]),
+                    torch.tensor([[1]]),
+                ],
+                dim=-1,
+            ),
+        ],
+        dim=-2,
+    ).squeeze(0)
 
 def multirz_eigvals(params, n_wires):
     """Compute eigenvalue for multiqubit RZ gate.
@@ -635,7 +775,7 @@ def rzx_matrix(params):
     theta = params.type(C_DTYPE)
     co = torch.cos(theta / 2)
     jsi = 1j * torch.sin(theta / 2)
-
+    
     matrix = (
         torch.tensor(
             [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
@@ -960,6 +1100,7 @@ def cu3_matrix(params):
     return matrix.squeeze(0)
 
 
+
 def qubitunitary_matrix(params):
     """Compute unitary matrix for Qubitunitary gate.
 
@@ -1101,6 +1242,56 @@ def singleexcitation_matrix(params):
     return matrix.squeeze(0)
 
 
+def qft_matrix(n_wires):
+    """Compute unitary matrix for QFT.
+
+    Args:
+        n_wires: the number of qubits
+    """
+    dimension = 2**n_wires
+    mat = torch.zeros((dimension,dimension), dtype=torch.complex64)
+    omega = np.exp(2 * np.pi * 1j / dimension)
+
+    for m in range(dimension):
+        for n in range(dimension):
+            mat[m, n] = omega ** (m * n)
+    mat = mat / np.sqrt(dimension)
+    return mat
+
+  
+def r_matrix(params: torch.Tensor) -> torch.Tensor:
+    """Compute unitary matrix for R gate.
+
+    Args:
+        params (torch.Tensor): The rotation angle.
+
+    Returns:
+        torch.Tensor: The computed unitary matrix.
+
+    """
+
+    theta = params.type(C_DTYPE)
+    phi = params.type(C_DTYPE)
+    exp = torch.exp(-1j * phi)
+    """
+    Seems to be a pytorch bug. Have to explicitly cast the theta to a
+    complex number. If directly theta = params, then get error:
+
+    allow_unreachable=True, accumulate_grad=True)  # allow_unreachable flag
+    RuntimeError: Expected isFloatingType(grad.scalar_type()) ||
+    (input_is_complex == grad_is_complex) to be true, but got false.
+    (Could this error message be improved?
+    If so, please report an enhancement request to PyTorch.)
+
+    """
+    co = torch.cos(theta / 2)
+    jsi = 1j * torch.sin(-theta / 2)
+
+    return torch.stack(
+        [torch.cat([co, exp*jsi], dim=-1), torch.cat([torch.conj(exp)*jsi, co], dim=-1)], dim=-2
+    ).squeeze(0)
+
+
 mat_dict = {
     "hadamard": torch.tensor(
         [[INV_SQRT2, INV_SQRT2], [INV_SQRT2, -INV_SQRT2]], dtype=C_DTYPE
@@ -1168,6 +1359,58 @@ def singleexcitation_matrix(params):
     ),
     "ecr": torch.tensor(
         [[0, 0, 1, 1j], [0, 0, 1j, 1], [1, -1j, 0, 0], [-1j, 1, 0, 0]], dtype=C_DTYPE
+    ),
+    "sdg": torch.tensor(
+        [[1, 0], [0, -1j]], dtype=C_DTYPE
+    ),
+    "tdg": torch.tensor(
+        [[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=C_DTYPE
+    ),
+    "sxdg": torch.tensor(
+        [[0.5-0.5j, 0.5+0.5j], [0.5+0.5j, 0.5-0.5j]], dtype=C_DTYPE
+     ),
+    "chadamard": torch.tensor(
+        [[1, 0, 0, 0],
+         [0, INV_SQRT2, 0, INV_SQRT2],
+         [0, 0, 1, 0],
+         [0, INV_SQRT2, 0, -INV_SQRT2]], dtype=C_DTYPE
+    ),
+    "iswap": torch.tensor(
+        [[1, 0, 0, 0],
+         [0, 1j, 0, 0],
+         [0, 0, 1j, 0],
+         [0, 0, 0, 1]], dtype=C_DTYPE
+    ),
+    "ccz": torch.tensor(
+        [
+            [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, 1, 0],
+            [0, 0, 0, 0, 0, 0, 0, -1],
+        ],
+        dtype=C_DTYPE,
+    ),
+    "cs": torch.tensor(
+        [[1, 0, 0, 0],
+         [0, 1, 0, 0],
+         [0, 0, 1, 0],
+         [0, 0, 0, 1j]], dtype=C_DTYPE
+    ),
+    "csdg": torch.tensor(
+        [[1, 0, 0, 0],
+         [0, 1, 0, 0],
+         [0, 0, 1, 0],
+         [0, 0, 0, -1j]], dtype=C_DTYPE
+    ),
+    "csx": torch.tensor(
+        [[1, 0, 0, 0],
+         [0, 0.5+0.5j, 0, 0.5-0.5j],
+         [0, 0, 1, 0],
+         [0, 0.5-0.5j, 0, 0.5+0.5j]], dtype=C_DTYPE
     )
     / np.sqrt(2),
     "rx": rx_matrix,
@@ -1196,6 +1439,13 @@ def singleexcitation_matrix(params):
     "multicnot": multicnot_matrix,
     "multixcnot": multixcnot_matrix,
     "singleexcitation": singleexcitation_matrix,
+    "qft": qft_matrix,
+    "dcx": torch.tensor(
+        [[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]], dtype=C_DTYPE
+    ),
+    "xxminyy": xxminyy_matrix,
+    "xxplusyy": xxplusyy_matrix,
+    "r": r_matrix,
 }
 
 
@@ -1528,6 +1778,7 @@ def s(
     )
 
 
+    
 def t(
     q_device,
     wires,
@@ -2373,6 +2624,98 @@ def rot(
         inverse=inverse,
     )
 
+def xxminyy(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+    """Perform the XXminusYY gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "xxminyy"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+
+def xxplusyy(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+    """Perform the XXPlusYY gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "xxplusyy"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
 
 def multirz(
     q_device,
@@ -3031,8 +3374,8 @@ def qubitunitarystrict(
         inverse=inverse,
     )
 
-
-def multicnot(
+    
+def c3x(
     q_device,
     wires,
     params=None,
@@ -3042,7 +3385,7 @@ def multicnot(
     inverse=False,
     comp_method="bmm",
 ):
-    """Perform the multi qubit cnot gate.
+    """Perform the c3x gate.
 
     Args:
         q_device (tq.QuantumDevice): The QuantumDevice.
@@ -3063,7 +3406,7 @@ def multicnot(
         None.
 
     """
-    name = "multicnot"
+    name = "qubitunitary"
     mat = mat_dict[name]
     gate_wrapper(
         name=name,
@@ -3071,15 +3414,14 @@ def multicnot(
         method=comp_method,
         q_device=q_device,
         wires=wires,
-        params=params,
+        params=mat_dict['toffoli'],
         n_wires=n_wires,
         static=static,
         parent_graph=parent_graph,
         inverse=inverse,
     )
 
-
-def multixcnot(
+def multicnot(
     q_device,
     wires,
     params=None,
@@ -3089,7 +3431,7 @@ def multixcnot(
     inverse=False,
     comp_method="bmm",
 ):
-    """Perform the multi qubit xcnot gate.
+    """Perform the multi qubit cnot gate.
 
     Args:
         q_device (tq.QuantumDevice): The QuantumDevice.
@@ -3110,7 +3452,7 @@ def multixcnot(
         None.
 
     """
-    name = "multixcnot"
+    name = "multicnot"
     mat = mat_dict[name]
     gate_wrapper(
         name=name,
@@ -3126,7 +3468,7 @@ def multixcnot(
     )
 
 
-def singleexcitation(
+def multixcnot(
     q_device,
     wires,
     params=None,
@@ -3136,7 +3478,7 @@ def singleexcitation(
     inverse=False,
     comp_method="bmm",
 ):
-    """Perform the single excitation gate.
+    """Perform the multi qubit xcnot gate.
 
     Args:
         q_device (tq.QuantumDevice): The QuantumDevice.
@@ -3157,7 +3499,7 @@ def singleexcitation(
         None.
 
     """
-    name = "singleexcitation"
+    name = "multixcnot"
     mat = mat_dict[name]
     gate_wrapper(
         name=name,
@@ -3173,7 +3515,7 @@ def singleexcitation(
     )
 
 
-def ecr(
+def singleexcitation(
     q_device,
     wires,
     params=None,
@@ -3183,8 +3525,7 @@ def ecr(
     inverse=False,
     comp_method="bmm",
 ):
-    """Perform the echoed cross-resonance gate.
-    https://qiskit.org/documentation/stubs/qiskit.circuit.library.ECRGate.html
+    """Perform the single excitation gate.
 
     Args:
         q_device (tq.QuantumDevice): The QuantumDevice.
@@ -3205,7 +3546,7 @@ def ecr(
         None.
 
     """
-    name = "ecr"
+    name = "singleexcitation"
     mat = mat_dict[name]
     gate_wrapper(
         name=name,
@@ -3221,25 +3562,627 @@ def ecr(
     )
 
 
-h = hadamard
-sh = shadamard
-x = paulix
-y = pauliy
-z = pauliz
-xx = rxx
-yy = ryy
-zz = rzz
-zx = rzx
-cx = cnot
-ccnot = toffoli
-ccx = toffoli
-u = u3
-cu = cu3
-p = phaseshift
+def ecr(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+    """Perform the echoed cross-resonance gate.
+    https://qiskit.org/documentation/stubs/qiskit.circuit.library.ECRGate.html
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "ecr"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+
+def qft(q_device,
+        wires,
+        params=None,
+        n_wires=None,
+        static=False,
+        parent_graph=None,
+        inverse=False,
+        comp_method="bmm",):
+    
+    name = "qft"
+    if n_wires == None:
+        wires = [wires] if isinstance(wires, int) else wires
+        n_wires = len(wires)
+   
+    mat = mat_dict[name]
+    # mat = qft_matrix(n_wires)
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+    
+
+def sdg(
+    q_device: QuantumDevice,
+    wires: Union[List[int], int],
+    params: torch.Tensor = None,
+    n_wires: int = None,
+    static: bool = False,
+    parent_graph=None,
+    inverse: bool = False,
+    comp_method: str = "bmm",
+):
+    """Perform the sdg gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "sdg"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def tdg(
+    q_device: QuantumDevice,
+    wires: Union[List[int], int],
+    params: torch.Tensor = None,
+    n_wires: int = None,
+    static: bool = False,
+    parent_graph=None,
+    inverse: bool = False,
+    comp_method: str = "bmm",
+):
+    """Perform the tdg gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "tdg"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def sxdg(
+    q_device: QuantumDevice,
+    wires: Union[List[int], int],
+    params: torch.Tensor = None,
+    n_wires: int = None,
+    static: bool = False,
+    parent_graph=None,
+    inverse: bool = False,
+    comp_method: str = "bmm",
+):
+    """Perform the sxdg gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "sxdg"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def chadamard(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the chadamard gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+
+    name = "chadamard"
+
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+
+def ccz(
+
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the ccz gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "ccz"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+
+def iswap(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+    """Perform the iswap gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "iswap"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def cs(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the cs gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+
+    name = "cs"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def csdg(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the csdg gate.
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+
+    Returns:
+        None.
+
+    """
+    name = "csdg"
+
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def csx(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the csx gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "csx"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+def dcx(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the dcx gate.
+
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "dcx"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+    
+def r(
+    q_device,
+    wires,
+    params=None,
+    n_wires=None,
+    static=False,
+    parent_graph=None,
+    inverse=False,
+    comp_method="bmm",
+):
+
+    """Perform the R gate.
+  
+    Args:
+        q_device (tq.QuantumDevice): The QuantumDevice.
+        wires (Union[List[int], int]): Which qubit(s) to apply the gate.
+        params (torch.Tensor, optional): Parameters (if any) of the gate.
+            Default to None.
+        n_wires (int, optional): Number of qubits the gate is applied to.
+            Default to None.
+        static (bool, optional): Whether use static mode computation.
+            Default to False.
+        parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
+            current operation. Default to None.
+        inverse (bool, optional): Whether inverse the gate. Default to False.
+        comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
+        matrix vector multiplication. Default to 'bmm'.
+
+    Returns:
+        None.
+
+    """
+    name = "r"
+    mat = mat_dict[name]
+    gate_wrapper(
+        name=name,
+        mat=mat,
+        method=comp_method,
+        q_device=q_device,
+        wires=wires,
+        params=params,
+        n_wires=n_wires,
+        static=static,
+        parent_graph=parent_graph,
+        inverse=inverse,
+    )
+
+
+h = hadamard
+sh = shadamard
+x = paulix
+y = pauliy
+z = pauliz
+xx = rxx
+yy = ryy
+zz = rzz
+zx = rzx
+cx = cnot
+ccnot = toffoli
+ccx = toffoli
+u = u3
+cu = cu3
+p = phaseshift
 cp = cu1
 cr = cu1
 cphase = cu1
 echoedcrossresonance = ecr
+ch = chadamard
+
 
 func_name_dict = {
     "hadamard": hadamard,
@@ -3304,4 +4247,21 @@ def ecr(
     "singleexcitation": singleexcitation,
     "ecr": ecr,
     "echoedcrossresonance": echoedcrossresonance,
+    "qft": qft,
+    "sdg": sdg,
+    "tdg": tdg,
+    "sxdg": sxdg,
+    "ch": ch,
+    "ccz": ccz,
+    "iswap": iswap,
+    "cs": cs,
+    "csdg": csdg,
+    "csx": csx,
+    "chadamard": chadamard,
+    "ccz": ccz,
+    "dcx": dcx,
+    "xxminyy":xxminyy,
+    "xxplusyy": xxplusyy,
+    "c3x":c3x,
+    "r": r,
 }
diff --git a/torchquantum/graph.py b/torchquantum/graph.py
index b0916678..01f4a49c 100644
--- a/torchquantum/graph.py
+++ b/torchquantum/graph.py
@@ -10,14 +10,38 @@
 
 
 def encode_w(wires):
+    """Encode a list of wires into a string representation.
+
+        Args:
+            wires (list[int]): List of wire indices.
+
+        Returns:
+            str: String representation of the wires.
+        """
     return ".".join(list(map(str, wires)))
 
 
 def decode_w(w):
+    """Decode a string representation of wires into a list.
+
+        Args:
+            w (str): String representation of wires.
+
+        Returns:
+            list[int]: List of wire indices.
+        """
     return list(map(eval, w.split(".")))
 
 
 def static_support(f):
+    """Decorator for static support in QuantumGraph class.
+
+        Args:
+            f (function): Function to be decorated.
+
+        Returns:
+            function: Decorated function.
+        """
     @functools.wraps(f)
     def forward_register_graph(*args, **kwargs):
         if args[0].static_mode and args[0].parent_graph is not None:
@@ -34,6 +58,28 @@ def forward_register_graph(*args, **kwargs):
 
 
 class QuantumGraph(object):
+    """
+       The QuantumGraph class is used to construct and execute a quantum graph
+       consisting of quantum operations.
+
+       Attributes:
+           is_list_finish (bool): Flag indicating if the module list construction is finished.
+           module_list (list): List of quantum operations in the graph.
+           flat_module_list (list): Flattened list of quantum operations in the graph.
+           wire_module_dict (dict): Dictionary mapping wires to the corresponding quantum operations.
+           wire_module_list (list): List of quantum operations for each wire.
+           local2global_wire_mapping (dict): Dictionary mapping local wire indices to global wire indices.
+           global2local_wire_mapping (dict): Dictionary mapping global wire indices to local wire indices.
+           schedules (list): List of schedules for executing the quantum operations.
+           q_device: Quantum device.
+           device: Device (CPU/GPU) for execution.
+           func_list (list): List of functional operations in the graph.
+           func_ptr (int): Pointer to the current functional operation.
+           num_func (int): Total number of functional operations.
+           build_finish (bool): Flag indicating if the graph construction is finished.
+           static_matrix_dict (dict): Dictionary mapping operation names to their static matrices.
+       """
+
     def __init__(self):
         self.is_list_finish = False
         self.module_list = []
@@ -53,6 +99,11 @@ def __init__(self):
         self.static_matrix_dict = {}
 
     def add_op(self, op: tq.Operator):
+        """Add a quantum operation to the graph.
+
+                Args:
+                    op (QuantumModule): Quantum operation to be added to the graph.
+                """
         if not self.is_list_finish:
             # graph construction is not finished, add the operation
             self.module_list.append(op)
@@ -63,6 +114,21 @@ def add_op(self, op: tq.Operator):
     def add_func(
         self, name, wires, parent_graph, params=None, n_wires=None, inverse=False
     ):
+        """Add a quantum function to the module.
+
+            If the graph construction is not finished, a new operation is built and added
+            to the graph. If the graph construction is finished, the parameters of the new
+            operation are updated.
+
+            Args:
+                name (str): Name of the quantum function.
+                wires (list[int]): List of wire indices used by the function.
+                parent_graph (QuantumGraph): Parent quantum graph of the function.
+                params (Any): Parameters of the function (optional).
+                n_wires (int): Number of wires used by the function (optional).
+                inverse (bool): Flag indicating if the function should be inverted (optional).
+            """
+
         if not self.is_list_finish:
             # graph construction is not finished, build a new operation and
             # add the operation to the graph
@@ -87,6 +153,21 @@ def add_func(
             self.func_ptr = (self.func_ptr + 1) % self.num_func
 
     def build(self, wires_per_block):
+        """Build the quantum module.
+
+           This method performs various steps to construct the quantum module, including:
+           - Building wire-module dictionary
+           - Building local wire-module list
+           - Building flat module list
+           - Building schedule blockwise
+           - Setting the build_finish flag
+
+           Args:
+               wires_per_block (int): Number of wires per block.
+
+           Returns:
+               None
+           """
         self.build_wire_module_dict(self.module_list)
         self.build_local_wire_module_list()
         self.build_flat_module_list(self.module_list)
@@ -95,6 +176,19 @@ def build(self, wires_per_block):
         self.build_finish = True
 
     def forward(self, wires_per_block):
+        """Forward pass of the quantum module.
+
+            If the module is not built yet, it calls the `build` method to construct
+            the quantum module. Then, it performs the following steps:
+            - Builds the static matrix
+            - Applies the unitary transformation
+
+            Args:
+                wires_per_block (int): Number of wires per block.
+
+            Returns:
+                None
+            """
         if not self.build_finish:
             self.build(wires_per_block)
 
@@ -102,6 +196,15 @@ def forward(self, wires_per_block):
         self.apply_unitary()
 
     def build_static_matrix(self):
+        """Builds the static matrix for the quantum module.
+
+            The method performs the following steps:
+            - Optimizes the static mode by sharing static matrices for fixed unitary operators and computing matrices for parameterized operators together
+            - Constructs the static matrices for each module based on their type and parameters
+
+            Returns:
+                None
+            """
         # the optimization to speedup the static mode
         # 1. fixed unitary operators will share one matrix
         # 2. parameterized operators, the matrices for the same type are
@@ -185,6 +288,19 @@ def build_static_matrix(self):
                 raise NotImplementedError(f"Module {name} not in list")
 
     def build_wire_module_dict(self, module_list):
+        """Builds a dictionary mapping wires to their associated modules.
+
+            The method recursively traverses the module list and constructs a dictionary
+            that maps each wire to the modules it belongs to. For leaf nodes, i.e., modules
+            with an empty module list, the wire is added to the dictionary. For non-leaf nodes,
+            the method recursively calls itself on the module list of the current module.
+
+            Args:
+                module_list (list[QuantumModule]): The list of modules to build the wire-module dictionary for.
+
+            Returns:
+                None
+            """
         for module in module_list:
             if len(module.graph.module_list) == 0:
                 # leaf node
@@ -197,6 +313,20 @@ def build_wire_module_dict(self, module_list):
                 self.build_wire_module_dict(module.graph.module_list)
 
     def build_flat_module_list(self, module_list=None):
+        """Builds a flat list of modules from a nested module list.
+
+           The method recursively traverses the module list and constructs a flat list
+           of modules by appending leaf nodes (modules without a nested module list)
+           to the flat module list. It also checks for modules with no operations and
+           logs a warning if found.
+
+           Args:
+               module_list (list[QuantumModule], optional): The module list to build the flat module list from.
+                   If not provided, the instance's `module_list` attribute is used as the default.
+
+           Returns:
+               None
+           """
         if module_list is None:
             module_list = self.module_list
         for module in module_list:
@@ -211,6 +341,14 @@ def build_flat_module_list(self, module_list=None):
                 self.build_flat_module_list(module.graph.module_list)
 
     def build_local_wire_module_list(self):
+        """Builds local wire module lists and wire mappings.
+
+            The method constructs local wire module lists by assigning local wire indices
+            to the global wires. It also builds mappings between local and global wire indices.
+
+            Returns:
+                None
+            """
         global_wires = sorted(list(self.wire_module_dict.keys()))
         local_wires = list(range(len(global_wires)))
 
@@ -220,6 +358,21 @@ def build_local_wire_module_list(self):
             self.wire_module_list.append(self.wire_module_dict[global_wire])
 
     def build_schedule_blockwise(self, wires_per_block):
+        """Builds the schedule for block-wise execution.
+
+          The method greedily searches for the block containing the highest number of gates per block.
+          The number of wires per block is specified as a hyperparameter. It iteratively adds gates to
+          the schedule until all gates have been scheduled.
+
+          Args:
+              wires_per_block (int): The number of wires in each block.
+
+          Returns:
+              list[dict]: A list of dictionaries representing the schedule. Each dictionary contains
+                          the wires involved in the block and the modules (gates) to be executed on
+                          those wires.
+
+          """
         # greedily search for the block containing most number of gates per
         # block. The block contains wires_per_block wires as a hyper-parameter
         n_wires = len(self.wire_module_list)
@@ -227,14 +380,37 @@ def build_schedule_blockwise(self, wires_per_block):
         wire_module_len = [len(wire_module) for wire_module in self.wire_module_list]
 
         def schedule_finish():
+            """Checks if the scheduling process is complete.
+
+                    Returns:
+                        bool: True if scheduling is complete, False otherwise.
+                    """
             return all(
                 [module_ptrs[k] == wire_module_len[k] for k in range(len(module_ptrs))]
             )
 
         def is_front(wires, ptrs):
+            """Checks if the gates specified by `wires` are at the front of their respective modules.
+
+                    Args:
+                        wires (list[int]): List of wire indices.
+                        ptrs (list[int]): List of pointers indicating the current position in the modules.
+
+                    Returns:
+                        bool: True if gates are at the front, False otherwise.
+                    """
             return len(set([id(self.wire_module_list[w][ptrs[w]]) for w in wires])) == 1
 
         def add_front_large_qubit_gate(ptrs, sches):
+            """Adds large qubit gates at the front of the schedule if the number of wires in a block is smaller.
+
+                    Args:
+                        ptrs (list[int]): List of pointers indicating the current position in the modules.
+                        sches (list[dict]): List of dictionaries representing the schedule.
+
+                    Returns:
+                        None
+                    """
             # deal with the case when the number of wires in a block is
             # smaller than the number of wires in a gate
             for w in range(n_wires):
@@ -257,9 +433,27 @@ def add_front_large_qubit_gate(ptrs, sches):
                             ptrs[local_w] += 1
 
         def comb_finish(combination, ptrs):
+            """Checks if all gates in the combination have been scheduled.
+
+                    Args:
+                        combination (list[int]): List of wire indices representing a combination of wires.
+                        ptrs (list[int]): List of pointers indicating the current position in the modules.
+
+                    Returns:
+                        bool: True if all gates in the combination have been scheduled, False otherwise.
+                    """
             return all(ptrs[w] == wire_module_len[w] for w in combination)
 
         def wires_in_comb(wires, combination):
+            """Checks if all wires are present in the combination.
+
+                   Args:
+                       wires (list[int]): List of wire indices.
+                       combination (list[int]): List of wire indices representing a combination of wires.
+
+                   Returns:
+                       bool: True if all wires are present in the combination, False otherwise.
+                   """
             return all([w in combination for w in wires])
 
         schedules = []
@@ -314,6 +508,17 @@ def wires_in_comb(wires, combination):
         return schedules
 
     def build_schedule_layerwise(self):
+        """Builds the schedule for layer-wise execution.
+
+            The method merges multiple consecutive operations applied to the same wires into one scheduled layer.
+            It iteratively adds operations to the schedule until all operations have been scheduled.
+
+            Returns:
+                list[dict]: A list of dictionaries representing the schedule. Each dictionary contains
+                            the wire combination involved in the layer and the operations to be executed
+                            on those wires.
+
+            """
         # if multiple consecutive gate are applied to the same wires,
         # then merge them together in one scheduled layer
         n_wires = len(self.wire_module_list)
@@ -322,11 +527,25 @@ def build_schedule_layerwise(self):
         wire_module_len = [len(wire_module) for wire_module in self.wire_module_list]
 
         def schedule_finish():
+            """Checks if the scheduling process is complete.
+
+                   Returns:
+                       bool: True if scheduling is complete, False otherwise.
+                   """
             return all(
                 [module_ptrs[k] == wire_module_len[k] for k in range(len(module_ptrs))]
             )
 
         def is_front(op, ptrs):
+            """Checks if the given operation is at the front of its respective module.
+
+                    Args:
+                        op (tq.Operator): The operation.
+                        ptrs (list[int]): List of pointers indicating the current position in the modules.
+
+                    Returns:
+                        bool: True if the operation is at the front, False otherwise.
+                    """
             ws = [self.global2local_wire_mapping[w] for w in op.wires]
             is_all_front = True
             for w in ws:
@@ -336,10 +555,28 @@ def is_front(op, ptrs):
             return is_all_front
 
         def update_ptrs(ws, ptrs):
+            """Updates the pointers for the given wires.
+
+                    Args:
+                        ws (list[int]): List of wire indices.
+                        ptrs (list[int]): List of pointers indicating the current position in the modules.
+
+                    Returns:
+                        None
+                    """
             for w in ws:
                 ptrs[w] += 1
 
         def has_conflict(wire_pairs, wires):
+            """Checks if there is a conflict between the given wire pairs and wires.
+
+                    Args:
+                        wire_pairs (list[tuple[int]]): List of wire pairs.
+                        wires (list[int]): List of wire indices.
+
+                    Returns:
+                        bool: True if there is a conflict, False otherwise.
+                    """
             if encode_w(wires) in wire_pairs:
                 conflict = False
             else:
@@ -450,6 +687,17 @@ def acc_m_unitary_einsum(u, wires, module):
 
     @staticmethod
     def acc_m_unitary_bmm(u, global_wires, module):
+        """Updates the unitary tensor using Einsum.
+
+           Args:
+               u (torch.Tensor): The input unitary tensor.
+               wires (list[int]): List of wire indices.
+               module (tq.Operator): The module (gate) applied to the wires.
+
+           Returns:
+               torch.Tensor: The updated unitary tensor.
+
+           """
         # compute the unitary of each block and apply to the device
         if u.dim() % 2 == 1:
             is_u_batch = True
@@ -532,6 +780,15 @@ def get_unitary(self):
         return self.get_schedule_unitary(self.schedules[0])
 
     def get_schedule_unitary(self, schedule):
+        """Computes the unitary for a given schedule.
+
+            Args:
+                schedule (dict): The schedule containing the wires and modules.
+
+            Returns:
+                torch.Tensor: The unitary tensor corresponding to the given schedule.
+
+            """
         # here some front large gates will need a larger unitary
         unitary = torch.eye(
             2**self.q_device.n_wires, dtype=C_DTYPE, device=self.device
@@ -553,6 +810,12 @@ def get_schedule_unitary(self, schedule):
         return unitary
 
     def apply_unitary(self):
+        """Applies the unitary transformations to the quantum device based on the schedules.
+
+            Returns:
+                None
+
+            """
         for schedule in self.schedules:
             comb = schedule["wires"]
             # here some front large gates will need a larger unitary
diff --git a/torchquantum/layers.py b/torchquantum/layers.py
index 12f35431..3b1779f3 100644
--- a/torchquantum/layers.py
+++ b/torchquantum/layers.py
@@ -29,12 +29,27 @@
 
 
 class QuantumModuleFromOps(tq.QuantumModule):
+    """Initializes a QuantumModuleFromOps instance.
+
+            Args:
+                ops (List[tq.Operation]): List of quantum operations.
+
+            """
     def __init__(self, ops):
         super().__init__()
         self.ops = tq.QuantumModuleList(ops)
 
     @tq.static_support
     def forward(self, q_device: tq.QuantumDevice):
+        """Performs the forward pass of the quantum module.
+
+               Args:
+                   q_device (tq.QuantumDevice): Quantum device to apply the operations on.
+
+               Returns:
+                   None
+
+               """
         self.q_device = q_device
         for op in self.ops:
             op(q_device)
@@ -68,6 +83,19 @@ def forward(self, q_device: tq.QuantumDevice):
 
 
 class ClassicalInOpAll(tq.QuantumModule):
+    """
+      Quantum module that applies the same quantum operation to all wires of a quantum device,
+      where the parameters of the operation are obtained from a classical input.
+
+      Args:
+          n_gate (int): Number of gates.
+          op (tq.Operator): Quantum operation to be applied.
+
+      Attributes:
+          n_gate (int): Number of gates.
+          gate_all (nn.ModuleList): List of quantum operations.
+
+      """
     def __init__(self, n_gate: int, op: tq.Operator):
         super().__init__()
         self.n_gate = n_gate
@@ -77,6 +105,20 @@ def __init__(self, n_gate: int, op: tq.Operator):
 
     @tq.static_support
     def forward(self, q_device: tq.QuantumDevice, x):
+        """
+               Performs the forward pass of the classical input quantum operation module.
+
+               Args:
+                   q_device (tq.QuantumDevice): Quantum device to apply the operations on.
+                   x (torch.Tensor): Classical input of shape (batch_size, n_gate).
+
+               Returns:
+                   None
+
+               Raises:
+                   AssertionError: If the number of gates is different from the number of wires in the device.
+
+               """
         # rx on all wires, assert the number of gate is the same as the number
         # of wires in the device.
         assert self.n_gate == q_device.n_wires, (
@@ -89,6 +131,18 @@ def forward(self, q_device: tq.QuantumDevice, x):
 
 
 class FixedOpAll(tq.QuantumModule):
+    """
+       Quantum module that applies the same fixed quantum operation to all wires of a quantum device.
+
+       Args:
+           n_gate (int): Number of gates.
+           op (tq.Operator): Quantum operation to be applied.
+
+       Attributes:
+           n_gate (int): Number of gates.
+           gate_all (nn.ModuleList): List of quantum operations.
+
+       """
     def __init__(self, n_gate: int, op: tq.Operator):
         super().__init__()
         self.n_gate = n_gate
@@ -98,6 +152,19 @@ def __init__(self, n_gate: int, op: tq.Operator):
 
     @tq.static_support
     def forward(self, q_device: tq.QuantumDevice):
+        """
+                Performs the forward pass of the fixed quantum operation module.
+
+                Args:
+                    q_device (tq.QuantumDevice): Quantum device to apply the operations on.
+
+                Returns:
+                    None
+
+                Raises:
+                    AssertionError: If the number of gates is different from the number of wires in the device.
+
+                """
         # rx on all wires, assert the number of gate is the same as the number
         # of wires in the device.
         assert self.n_gate == q_device.n_wires, (
@@ -110,6 +177,18 @@ def forward(self, q_device: tq.QuantumDevice):
 
 
 class TwoQAll(tq.QuantumModule):
+    """
+        Quantum module that applies a two-qubit quantum operation to adjacent pairs of wires in a quantum device.
+
+        Args:
+            n_gate (int): Number of adjacent pairs of wires.
+            op (tq.Operator): Two-qubit quantum operation to be applied.
+
+        Attributes:
+            n_gate (int): Number of adjacent pairs of wires.
+            op (tq.Operator): Two-qubit quantum operation.
+
+        """
     def __init__(self, n_gate: int, op: tq.Operator):
         super().__init__()
         self.n_gate = n_gate
@@ -123,6 +202,29 @@ def forward(self, q_device: tq.QuantumDevice):
 
 
 class RandomLayer(tq.QuantumModule):
+    """
+        Quantum module that represents a random layer of quantum operations applied to specified wires.
+
+        Args:
+            wires (int or Iterable[int]): Indices of the wires the operations are applied to.
+            n_ops (int): Number of random operations in the layer.
+            n_params (int): Number of parameters for each random operation.
+            op_ratios (list or float): Ratios determining the relative frequencies of different operation types.
+            op_types (tuple or tq.Operator): Types of random operations to be included in the layer.
+            seed (int): Seed for random number generation.
+            qiskit_compatible (bool): Flag indicating whether the layer should be compatible with Qiskit.
+
+        Attributes:
+            n_ops (int): Number of random operations in the layer.
+            n_params (int): Number of parameters for each random operation.
+            wires (list): Indices of the wires the operations are applied to.
+            n_wires (int): Number of wires.
+            op_types (list): Types of random operations included in the layer.
+            op_ratios (numpy.array): Ratios determining the relative frequencies of different operation types.
+            seed (int): Seed for random number generation.
+            op_list (tq.QuantumModuleList): List of random operations in the layer.
+
+        """
     def __init__(
         self,
         wires,
@@ -172,7 +274,17 @@ def __init__(
         self.build_random_layer()
 
     def rebuild_random_layer_from_op_list(self, n_ops_in, wires_in, op_list_in):
-        """Used for loading random layer from checkpoint"""
+        """
+           Rebuilds a random layer from the given operation list.
+           This method is used for loading a random layer from a checkpoint.
+
+           Args:
+               n_ops_in (int): Number of operations in the layer.
+               wires_in (list): Indices of the wires the operations are applied to.
+               op_list_in (list): List of operations in the layer.
+
+           """
+
         self.n_ops = n_ops_in
         self.wires = wires_in
         self.op_list = tq.QuantumModuleList()
@@ -241,6 +353,21 @@ def forward(self, q_device: tq.QuantumDevice):
 
 
 class RandomLayerAllTypes(RandomLayer):
+    """
+       Random layer with a wide range of quantum gate types.
+
+       This class extends the `RandomLayer` class to include a variety of quantum gate types as options for the random layer.
+
+       Args:
+           wires (int or list): Indices of the wires the operations are applied to.
+           n_ops (int): Number of operations in the layer.
+           n_params (int): Number of parameters for each operation.
+           op_ratios (list): Ratios for selecting different types of operations.
+           op_types (tuple): Types of operations to include in the layer.
+           seed (int): Seed for the random number generator.
+           qiskit_compatible (bool): Flag indicating whether the layer should be Qiskit-compatible.
+
+       """
     def __init__(
         self,
         wires,
@@ -294,6 +421,15 @@ def __init__(
 
 
 class SimpleQLayer(tq.QuantumModule):
+    """
+       Simple quantum layer consisting of three parameterized gates applied to specific wires.
+
+       This class represents a simple quantum layer with three parameterized gates: RX, RY, and RZ. The gates are applied to specific wires in the quantum device.
+
+       Args:
+           n_wires (int): Number of wires in the quantum device.
+
+       """
     def __init__(self, n_wires):
         super().__init__()
         self.n_wires = n_wires
@@ -312,6 +448,15 @@ def forward(self, q_dev):
 
 
 class CXLayer(tq.QuantumModule):
+    """
+        Quantum layer with a controlled-X (CX) gate applied to two specified wires.
+
+        This class represents a quantum layer with a controlled-X (CX) gate applied to two specified wires in the quantum device.
+
+        Args:
+            n_wires (int): Number of wires in the quantum device.
+
+        """
     def __init__(self, n_wires):
         super().__init__()
         self.n_wires = n_wires
@@ -323,6 +468,15 @@ def forward(self, q_dev):
 
 
 class CXCXCXLayer(tq.QuantumModule):
+    """
+       Quantum layer with a sequence of CX gates applied to three specified wires.
+
+       This class represents a quantum layer with a sequence of CX gates applied to three specified wires in the quantum device.
+
+       Args:
+           n_wires (int): Number of wires in the quantum device.
+
+       """
     def __init__(self, n_wires):
         super().__init__()
         self.n_wires = n_wires
@@ -336,6 +490,15 @@ def forward(self, q_dev):
 
 
 class SWAPSWAPLayer(tq.QuantumModule):
+    """
+       Quantum layer with a sequence of SWAP gates applied to two specified pairs of wires.
+
+       This class represents a quantum layer with a sequence of SWAP gates applied to two specified pairs of wires in the quantum device.
+
+       Args:
+           n_wires (int): Number of wires in the quantum device.
+
+       """
     def __init__(self, n_wires):
         super().__init__()
         self.n_wires = n_wires
@@ -348,6 +511,18 @@ def forward(self, q_dev):
 
 
 class Op1QAllLayer(tq.QuantumModule):
+    """
+        Quantum layer applying the same single-qubit operation to all wires.
+
+        This class represents a quantum layer that applies the same single-qubit operation to all wires in the quantum device.
+
+        Args:
+            op (tq.Operator): Single-qubit operation to be applied.
+            n_wires (int): Number of wires in the quantum device.
+            has_params (bool, optional): Flag indicating if the operation has parameters. Defaults to False.
+            trainable (bool, optional): Flag indicating if the operation is trainable. Defaults to False.
+
+        """
     def __init__(self, op, n_wires: int, has_params=False, trainable=False):
         super().__init__()
         self.n_wires = n_wires
@@ -363,6 +538,22 @@ def forward(self, q_device):
 
 
 class Op2QAllLayer(tq.QuantumModule):
+    """
+        Quantum layer applying the same two-qubit operation to all pairs of adjacent wires.
+        This class represents a quantum layer that applies the same two-qubit operation to all pairs of adjacent wires
+        in the quantum device. The pairs of wires can be determined in a circular or non-circular pattern based on the
+        specified jump.
+
+        Args:
+            op (tq.Operator): Two-qubit operation to be applied.
+            n_wires (int): Number of wires in the quantum device.
+            has_params (bool, optional): Flag indicating if the operation has parameters. Defaults to False.
+            trainable (bool, optional): Flag indicating if the operation is trainable. Defaults to False.
+            wire_reverse (bool, optional): Flag indicating if the order of wires in each pair should be reversed. Defaults to False.
+            jump (int, optional): Number of positions to jump between adjacent pairs of wires. Defaults to 1.
+            circular (bool, optional): Flag indicating if the pattern should be circular. Defaults to False.
+
+    """
     """pattern:
     circular = False
     jump = 1: [0, 1], [1, 2], [2, 3], [3, 4], [4, 5]
@@ -416,6 +607,21 @@ def forward(self, q_device):
 
 
 class Op2QFit32Layer(tq.QuantumModule):
+    """
+        Quantum layer applying the same two-qubit operation to all pairs of adjacent wires, fitting to 32 operations.
+
+        This class represents a quantum layer that applies the same two-qubit operation to all pairs of adjacent wires in the quantum device. The pairs of wires can be determined in a circular or non-circular pattern based on the specified jump. The layer is designed to fit to 32 operations by repeating the same operation pattern multiple times.
+
+        Args:
+            op (tq.Operator): Two-qubit operation to be applied.
+            n_wires (int): Number of wires in the quantum device.
+            has_params (bool, optional): Flag indicating if the operation has parameters. Defaults to False.
+            trainable (bool, optional): Flag indicating if the operation is trainable. Defaults to False.
+            wire_reverse (bool, optional): Flag indicating if the order of wires in each pair should be reversed. Defaults to False.
+            jump (int, optional): Number of positions to jump between adjacent pairs of wires. Defaults to 1.
+            circular (bool, optional): Flag indicating if the pattern should be circular. Defaults to False.
+
+        """
     def __init__(
         self,
         op,
@@ -454,6 +660,19 @@ def forward(self, q_device):
 
 
 class Op2QButterflyLayer(tq.QuantumModule):
+    """
+        Quantum layer applying the same two-qubit operation in a butterfly pattern.
+
+        This class represents a quantum layer that applies the same two-qubit operation in a butterfly pattern. The butterfly pattern connects the first and last wire, the second and second-to-last wire, and so on, until the center wire(s) in the case of an odd number of wires.
+
+        Args:
+            op (tq.Operator): Two-qubit operation to be applied.
+            n_wires (int): Number of wires in the quantum device.
+            has_params (bool, optional): Flag indicating if the operation has parameters. Defaults to False.
+            trainable (bool, optional): Flag indicating if the operation is trainable. Defaults to False.
+            wire_reverse (bool, optional): Flag indicating if the order of wires in each pair should be reversed. Defaults to False.
+
+        """
     """pattern: [0, 5], [1, 4], [2, 3]"""
 
     def __init__(
@@ -479,6 +698,19 @@ def forward(self, q_device):
 
 
 class Op2QDenseLayer(tq.QuantumModule):
+    """
+       Quantum layer applying the same two-qubit operation in a dense pattern.
+
+       This class represents a quantum layer that applies the same two-qubit operation in a dense pattern. The dense pattern connects every pair of wires, ensuring that each wire is connected to every other wire exactly once.
+
+       Args:
+           op (tq.Operator): Two-qubit operation to be applied.
+           n_wires (int): Number of wires in the quantum device.
+           has_params (bool, optional): Flag indicating if the operation has parameters. Defaults to False.
+           trainable (bool, optional): Flag indicating if the operation is trainable. Defaults to False.
+           wire_reverse (bool, optional): Flag indicating if the order of wires in each pair should be reversed. Defaults to False.
+
+       """
     """pattern:
     [0, 1], [0, 2], [0, 3], [0, 4], [0, 5]
     [1, 2], [1, 3], [1, 4], [1, 5]
@@ -513,6 +745,24 @@ def forward(self, q_device):
 
 
 class LayerTemplate0(tq.QuantumModule):
+    """
+       A template for a custom quantum layer.
+
+       Args:
+           arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+       Attributes:
+           n_wires (int): The number of wires in the layer.
+           arch (dict): The architecture configuration for the layer.
+           n_blocks (int): The number of blocks in the layer. (Optional)
+           n_layers_per_block (int): The number of layers per block. (Optional)
+           layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+       Methods:
+           build_layers: Abstract method to build the layers of the template.
+           forward: Applies the quantum layer to the given quantum device.
+
+       """
     def __init__(self, arch: dict = None):
         super().__init__()
         self.n_wires = arch["n_wires"]
@@ -534,7 +784,26 @@ def forward(self, q_device: tq.QuantumDevice):
 
 
 class U3CU3Layer0(LayerTemplate0):
-    """u3 cu3 blocks"""
+    """
+        Layer template with U3 and CU3 blocks.
+
+        This layer template consists of U3 and CU3 blocks repeated for the specified number of blocks.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the U3 and CU3 layers for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
+
 
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
@@ -558,7 +827,26 @@ def build_layers(self):
 
 
 class CU3Layer0(LayerTemplate0):
-    """u3 cu3 blocks"""
+    """
+        Layer template with CU3 blocks.
+
+        This layer template consists of CU3 blocks repeated for the specified number of blocks.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the CU3 layers for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
+
 
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
@@ -577,7 +865,26 @@ def build_layers(self):
 
 
 class CXRZSXLayer0(LayerTemplate0):
-    """CXRZSX blocks"""
+    """
+        Layer template with CXRZSX blocks.
+
+        This layer template consists of CXRZSX blocks, which include RZ, CNOT, and SX gates, repeated for the specified number of blocks.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the CXRZSX layers for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
+
 
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
@@ -610,6 +917,25 @@ def build_layers(self):
 
 
 class SethLayer0(LayerTemplate0):
+    """
+        Layer template with Seth blocks.
+
+        This layer template consists of Seth blocks, which include RZZ and RY gates, repeated for the specified number of blocks.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the Seth layers for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -632,6 +958,25 @@ def build_layers(self):
 
 
 class SethLayer1(LayerTemplate0):
+    """
+       Layer template with extended Seth blocks.
+
+       This layer template consists of extended Seth blocks, which include RZZ and RY gates repeated twice for each block.
+
+       Args:
+           arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+       Attributes:
+           n_wires (int): The number of wires in the layer.
+           arch (dict): The architecture configuration for the layer.
+           n_blocks (int): The number of blocks in the layer.
+           layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+       Methods:
+           build_layers: Builds the extended Seth layers for the template.
+           forward: Applies the quantum layer to the given quantum device.
+
+       """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -664,6 +1009,25 @@ def build_layers(self):
 
 
 class SethLayer2(LayerTemplate0):
+    """
+        Layer template with Seth blocks using Op2QFit32Layer.
+
+        This layer template consists of Seth blocks using the Op2QFit32Layer, which includes RZZ gates and supports 32 wires.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the Seth layers with Op2QFit32Layer for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -681,6 +1045,25 @@ def build_layers(self):
 
 
 class RZZLayer0(LayerTemplate0):
+    """
+        Layer template with RZZ blocks.
+
+        This layer template consists of RZZ blocks using the Op2QAllLayer.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the RZZ layers with Op2QAllLayer for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -698,6 +1081,25 @@ def build_layers(self):
 
 
 class BarrenLayer0(LayerTemplate0):
+    """
+        Layer template with Barren blocks.
+
+        This layer template consists of Barren blocks using the Op1QAllLayer and Op2QAllLayer.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the Barren layers with Op1QAllLayer and Op2QAllLayer for the template.
+            forward: Applies the quantum layer to the given quantum device.
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         layers_all.append(
@@ -727,6 +1129,25 @@ def build_layers(self):
 
 
 class FarhiLayer0(LayerTemplate0):
+    """
+     Layer template with Farhi blocks.
+
+     This layer template consists of Farhi blocks using the Op2QAllLayer.
+
+     Args:
+         arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+     Attributes:
+         n_wires (int): The number of wires in the layer.
+         arch (dict): The architecture configuration for the layer.
+         n_blocks (int): The number of blocks in the layer.
+         layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+     Methods:
+         build_layers: Builds the Farhi layers with Op2QAllLayer for the template.
+         forward: Applies the quantum layer to the given quantum device.
+
+     """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -754,6 +1175,25 @@ def build_layers(self):
 
 
 class MaxwellLayer0(LayerTemplate0):
+    """
+       Layer template with Maxwell blocks.
+
+       This layer template consists of Maxwell blocks using the Op1QAllLayer and Op2QAllLayer modules.
+
+       Args:
+           arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+       Attributes:
+           n_wires (int): The number of wires in the layer.
+           arch (dict): The architecture configuration for the layer.
+           n_blocks (int): The number of blocks in the layer.
+           layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+       Methods:
+           build_layers: Builds the Maxwell layers with Op1QAllLayer and Op2QAllLayer for the template.
+           forward: Applies the quantum layer to the given quantum device.
+
+       """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -807,6 +1247,25 @@ def build_layers(self):
 
 
 class RYRYCXLayer0(LayerTemplate0):
+    """
+       Layer template with RYRYCX blocks.
+
+       This layer template consists of RYRYCX blocks using the Op1QAllLayer and CXLayer modules.
+
+       Args:
+           arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+       Attributes:
+           n_wires (int): The number of wires in the layer.
+           arch (dict): The architecture configuration for the layer.
+           n_blocks (int): The number of blocks in the layer.
+           layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+       Methods:
+           build_layers: Builds the RYRYCX layers with Op1QAllLayer and CXLayer for the template.
+           forward: Applies the quantum layer to the given quantum device.
+
+       """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -820,6 +1279,25 @@ def build_layers(self):
 
 
 class RYRYRYCXCXCXLayer0(LayerTemplate0):
+    """
+       Layer template with RYRYRYCXCXCX blocks.
+
+       This layer template consists of RYRYRYCXCXCX blocks using the RYRYCXCXLayer module.
+
+       Args:
+           arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+       Attributes:
+           n_wires (int): The number of wires in the layer.
+           arch (dict): The architecture configuration for the layer.
+           n_blocks (int): The number of blocks in the layer.
+           layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+       Methods:
+           build_layers: Builds the RYRYRYCXCXCX layers with RYRYCXCXLayer for the template.
+
+       """
+
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -833,6 +1311,25 @@ def build_layers(self):
 
 
 class RYRYRYLayer0(LayerTemplate0):
+    """
+        Layer template with RYRYRYCXCXCX blocks.
+
+        This layer template consists of RYRYRYCXCXCX blocks using the Op1QAllLayer and CXCXCXLayer modules.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the RYRYRYCXCXCX layers with Op1QAllLayer and CXCXCXLayer for the template.
+
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -845,6 +1342,25 @@ def build_layers(self):
 
 
 class RYRYRYSWAPSWAPLayer0(LayerTemplate0):
+    """
+        Layer template with RYRYRYSWAPSWAP blocks.
+
+        This layer template consists of RYRYRYSWAPSWAP blocks using the Op1QAllLayer and SWAPSWAPLayer modules.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the RYRYRYSWAPSWAP layers with Op1QAllLayer and SWAPSWAPLayer for the template.
+
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -858,6 +1374,25 @@ def build_layers(self):
 
 
 class SWAPSWAPLayer0(LayerTemplate0):
+    """
+        Layer template with SWAPSWAP blocks.
+
+        This layer template consists of SWAPSWAP blocks using the SWAPSWAPLayer module.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the SWAPSWAP layers with SWAPSWAPLayer for the template.
+
+        """
+
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
@@ -866,6 +1401,24 @@ def build_layers(self):
 
 
 class RXYZCXLayer0(LayerTemplate0):
+    """
+        Layer template with RXYZCX blocks.
+
+        This layer template consists of RXYZCX blocks using the RXYZCXLayer module.
+
+        Args:
+            arch (dict, optional): The architecture configuration for the layer. Defaults to None.
+
+        Attributes:
+            n_wires (int): The number of wires in the layer.
+            arch (dict): The architecture configuration for the layer.
+            n_blocks (int): The number of blocks in the layer.
+            layers_all (tq.QuantumModuleList): The list of layers in the template.
+
+        Methods:
+            build_layers: Builds the RXYZCX layers with RXYZCXLayer for the template.
+
+        """
     def build_layers(self):
         layers_all = tq.QuantumModuleList()
         for k in range(self.arch["n_blocks"]):
diff --git a/torchquantum/measurement.py b/torchquantum/measurement.py
index 1fa8cf20..eb9457aa 100644
--- a/torchquantum/measurement.py
+++ b/torchquantum/measurement.py
@@ -29,14 +29,32 @@
 
 
 def gen_bitstrings(n_wires):
+    """Generate all possible bitstrings of length `n_wires`.
+
+    Args:
+        n_wires (int): The number of wires or bits.
+
+    Returns:
+        List[str]: A list of all possible bitstrings.
+
+    Example:
+        >>> gen_bitstrings(2)
+        ['00', '01', '10', '11']
+
+        >>> gen_bitstrings(3)
+        ['000', '001', '010', '011', '100', '101', '110', '111']
+    """
+    
     return ["{:0{}b}".format(k, n_wires) for k in range(2**n_wires)]
 
 
 def measure(qdev, n_shots=1024):
     """Measure the target state and obtain classical bitstream distribution
+    
     Args:
         q_state: input tq.QuantumDevice
         n_shots: number of simulated shots
+    
     Returns:
         distribution of bitstrings
     """
@@ -69,8 +87,24 @@ def measure(qdev, n_shots=1024):
 
 
 def find_observable_groups(observables):
-    # the group is not unique
-    # ["XXII", "IIZZ", "ZZII"] can be grouped as ["XXZZ", "ZZII"] or ["ZZZZ", "XXZZ"]
+    """Find groups of observables based on their similarity.
+
+    Some groups are not unique. Observables that can be transformed into each other
+    by swapping or replacing non-identity terms can belong to the same group.
+
+    Args:
+        observables (List[str]): A list of observables.
+
+    Returns:
+        Dict[str, List[str]]: A dictionary where the keys represent the groups
+            and the values are lists of observables in each group.
+
+    Example:
+        # ["XXII", "IIZZ", "ZZII"] can be grouped as ["XXZZ", "ZZII"] or ["ZZZZ", "XXZZ"]
+        >>> observables = ["XXII", "IIZZ", "ZZII"]
+        >>> find_observable_groups(observables)
+    """
+    
     groups = {}
     for observable in observables:
         matched = False
@@ -108,7 +142,23 @@ def expval_joint_sampling_grouping(
     qdev: tq.QuantumDevice,
     observables: List[str],
     n_shots_per_group=1024,
-):
+) -> dict:
+    """Compute the expectation value of observables using joint sampling and grouping.
+
+    Args:
+        qdev (tq.QuantumDevice): The quantum device.
+        observables (List[str]): A list of observable strings.
+        n_shots_per_group (int): Number of shots per group for measurement. Default is 1024.
+
+    Returns:
+        dict: A dictionary where the keys are the observable strings and the values are the expectation values.
+
+    Example:
+        >>> observables = ["XXII", "IIZZ", "ZZII"]
+        >>> qdev = tq.QuantumDevice(n_wires=4)
+        >>> expval_joint_sampling_grouping(qdev, observables)
+    """
+    
     assert len(observables) == len(set(observables)), "each observable should be unique"
     # key is the group, values is the list of sub-observables  
     obs = []
@@ -164,28 +214,31 @@ def expval_joint_sampling(
     observable: str,
     n_shots=1024,
 ):
-    """
-    Compute the expectation value of a joint observable from sampling 
-    the measurement bistring
+    """Compute the expectation value of a joint observable from sampling 
+    the measurement bistring.
+    
     Args:
-        qdev: the quantum device
-        observable: the joint observable, on the qubit 0, 1, 2, 3, etc in this order
+        qdev (tq.QuantumDevice): The quantum device.
+        observable (str): The joint observable, on the qubit 0, 1, 2, 3, etc in this order.
+        
     Returns:
         the expectation value
+        
     Examples:
-    >>> import torchquantum as tq
-    >>> import torchquantum.functional as tqf
-    >>> x = tq.QuantumDevice(n_wires=2)
-    >>> tqf.hadamard(x, wires=0)
-    >>> tqf.x(x, wires=1)
-    >>> tqf.cnot(x, wires=[0, 1])
-    >>> print(expval_joint_sampling(x, 'II', n_shots=8192))
-    tensor([[0.9997]])
-    >>> print(expval_joint_sampling(x, 'XX', n_shots=8192))
-    tensor([[0.9991]])
-    >>> print(expval_joint_sampling(x, 'ZZ', n_shots=8192))
-    tensor([[-0.9980]])
+        >>> import torchquantum as tq
+        >>> import torchquantum.functional as tqf
+        >>> x = tq.QuantumDevice(n_wires=2)
+        >>> tqf.hadamard(x, wires=0)
+        >>> tqf.x(x, wires=1)
+        >>> tqf.cnot(x, wires=[0, 1])
+        >>> print(expval_joint_sampling(x, 'II', n_shots=8192))
+        tensor([[0.9997]])
+        >>> print(expval_joint_sampling(x, 'XX', n_shots=8192))
+        tensor([[0.9991]])
+        >>> print(expval_joint_sampling(x, 'ZZ', n_shots=8192))
+        tensor([[-0.9980]])
     """
+    
     # rotation to the desired basis
     n_wires = qdev.n_wires
     paulix = op_name_dict["paulix"]
@@ -227,28 +280,31 @@ def expval_joint_analytical(
     qdev: tq.QuantumDevice,
     observable: str,
 ):
-    """
-    Compute the expectation value of a joint observable in analytical way, assuming the
+    """Compute the expectation value of a joint observable in analytical way, assuming the
     statevector is available.
+    
     Args:
-        qdev: the quantum device
-        observable: the joint observable, on the qubit 0, 1, 2, 3, etc in this order
+        qdev (tq.QuantumDevice): The quantum device.
+        observable (str): The joint observable, on the qubit 0, 1, 2, 3, etc in this order.
+        
     Returns:
         the expectation value
+        
     Examples:
-    >>> import torchquantum as tq
-    >>> import torchquantum.functional as tqf
-    >>> x = tq.QuantumDevice(n_wires=2)
-    >>> tqf.hadamard(x, wires=0)
-    >>> tqf.x(x, wires=1)
-    >>> tqf.cnot(x, wires=[0, 1])
-    >>> print(expval_joint_analytical(x, 'II'))
-    tensor([[1.0000]])
-    >>> print(expval_joint_analytical(x, 'XX'))
-    tensor([[1.0000]])
-    >>> print(expval_joint_analytical(x, 'ZZ'))
-    tensor([[-1.0000]])
+        >>> import torchquantum as tq
+        >>> import torchquantum.functional as tqf
+        >>> x = tq.QuantumDevice(n_wires=2)
+        >>> tqf.hadamard(x, wires=0)
+        >>> tqf.x(x, wires=1)
+        >>> tqf.cnot(x, wires=[0, 1])
+        >>> print(expval_joint_analytical(x, 'II'))
+        tensor([[1.0000]])
+        >>> print(expval_joint_analytical(x, 'XX'))
+        tensor([[1.0000]])
+        >>> print(expval_joint_analytical(x, 'ZZ'))
+        tensor([[-1.0000]])
     """
+    
     # compute the hamiltonian matrix
     paulix = mat_dict["paulix"]
     pauliy = mat_dict["pauliy"]
@@ -277,8 +333,24 @@ def expval(
     qdev: tq.QuantumDevice,
     wires: Union[int, List[int]],
     observables: Union[tq.Observable, List[tq.Observable]],
-):
+) -> torch.Tensor:
+    """Compute the expectation value of observables on specified wires.
 
+    Args:
+        qdev (tq.QuantumDevice): The quantum device.
+        wires (Union[int, List[int]]): The wire indices or a list of wire indices on which to compute the expectation values.
+        observables (Union[tq.Observable, List[tq.Observable]]): The observables or a list of observables to compute the expectation values.
+
+    Returns:
+        torch.Tensor: The expectation values.
+
+    Example:
+        >>> qdev = tq.QuantumDevice(n_wires=3)
+        >>> wires = [0, 1]
+        >>> observables = [tq.PauliX(), tq.PauliY()]
+        >>> expval(qdev, wires, observables)
+    """
+    
     all_dims = np.arange(qdev.states.dim())
     if isinstance(wires, int):
         wires = [wires]
@@ -308,14 +380,49 @@ def expval(
 
 
 class MeasureAll(tq.QuantumModule):
-    """Obtain the expectation value of all the qubits."""
+    """Obtain the expectation value of all the qubits.
 
+    Attributes:
+        obs (tq.Observable): The observable to compute the expectation value.
+        v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+
+    Methods:
+        __init__(obs, v_c_reg_mapping=None): Initialize the MeasureAll module.
+        
+        forward(qdev): Compute the expectation value of the observable on all qubits.
+        
+        set_v_c_reg_mapping(mapping): Set the mapping of classical register indices to virtual qubit indices.
+    """
+    
     def __init__(self, obs, v_c_reg_mapping=None):
+        """Initialize the MeasureAll module.
+
+        Args:
+            obs (tq.Observable): The observable to compute the expectation value.
+            v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+                Defaults to None.
+        """
+        
         super().__init__()
         self.obs = obs
         self.v_c_reg_mapping = v_c_reg_mapping
 
-    def forward(self, qdev: tq.QuantumDevice):
+    def forward(self, qdev: tq.QuantumDevice) -> torch.Tensor:
+        """Compute the expectation value of the observable on all qubits.
+
+        Args:
+            qdev (tq.QuantumDevice): The quantum device.
+
+        Returns:
+            torch.Tensor: The expectation values.
+
+        Example:
+            >>> module = MeasureAll(tq.PauliZ())
+            >>> qdev = tq.QuantumDevice(n_wires=3)
+            >>> result = module(qdev)
+            >>> print(result)
+        """
+        
         x = expval(qdev, list(range(qdev.n_wires)), [self.obs()] * qdev.n_wires)
 
         if self.v_c_reg_mapping is not None:
@@ -334,27 +441,73 @@ def forward(self, qdev: tq.QuantumDevice):
         else:
             return x
 
-    def set_v_c_reg_mapping(self, mapping):
+    def set_v_c_reg_mapping(self, mapping) -> None:
+        """Set the mapping of classical register indices to virtual qubit indices.
+
+        Args:
+            mapping (Dict[int, int]): The mapping dictionary.
+            
+        Returns:
+            None.
+
+        Example:
+            >>> module = MeasureAll(tq.PauliZ())
+            >>> mapping = {0: 2, 1: 0, 2: 1}
+            >>> module.set_v_c_reg_mapping(mapping)
+        """
+        
         self.v_c_reg_mapping = mapping
 
 
 class MeasureMultipleTimes(tq.QuantumModule):
-    """
-    obs list:
-    list of dict: example
-    [{'wires': [0, 2, 3, 1], 'observables': ['x', 'y', 'z', 'i']
-    },
-    {'wires': [0, 2, 3, 1], 'observables': ['x', 'y', 'z', 'i']
-    },
-    ]
+    """A quantum module to measure observables multiple times.
+
+    Attributes:
+        obs_list (List[Dict]): The list of observables and wires to measure.
+        v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+
+    Methods:
+        __init__(obs_list, v_c_reg_mapping=None): Initialize the MeasureMultipleTimes module.
+        forward(qdev): Measure the observables multiple times.
+
+    Examples:
+        obs_list = [
+            {
+                "wires": [0, 2, 3, 1],
+                "observables": ["x", "y", "z", "i"]
+            },
+            {
+                "wires": [0, 2, 3, 1],
+                "observables": ["x", "y", "z", "i"]
+            },
+        ]
+        measure_module = MeasureMultipleTimes(obs_list)
+        result = measure_module(qdev)
     """
 
     def __init__(self, obs_list, v_c_reg_mapping=None):
+        """Initialize the MeasureMultipleTimes module.
+
+        Args:
+            obs_list (List[Dict]): The list of observables and wires to measure.
+            v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+        """
+        
         super().__init__()
         self.obs_list = obs_list
         self.v_c_reg_mapping = v_c_reg_mapping
 
-    def forward(self, qdev: tq.QuantumDevice):
+    def forward(self, qdev: tq.QuantumDevice) -> torch.Tensor:
+        """
+        Measure the observables multiple times.
+
+        Args:
+            qdev (tq.QuantumDevice): The quantum device.
+
+        Returns:
+            torch.Tensor: The measurement results.
+        """
+        
         res_all = []
 
         for layer in self.obs_list:
@@ -395,22 +548,45 @@ def set_v_c_reg_mapping(self, mapping):
 
 
 class MeasureMultiPauliSum(tq.QuantumModule):
-    """
-    similar to qiskit.opflow PauliSumOp
-    obs list:
-    list of dict: example
-    [{'wires': [0, 2, 3, 1],
-    'observables': ['x', 'y', 'z', 'i'],
-    'coefficient': [1, 0.5, 0.4, 0.3]
-    },
-    {'wires': [0, 2, 3, 1],
-    'observables': ['x', 'y', 'z', 'i'],
-    'coefficient': [1, 0.5, 0.4, 0.3]
-    },
-    ]
+    """Measure a sum of multi-qubit Pauli operators.
+    
+    Similar to qiskit.opflow PauliSumOp.
+
+    Attributes:
+        obs_list (List[Dict]): The list of Pauli operators to measure along with their coefficients.
+        v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+        measure_multiple_times (MeasureMultipleTimes): The module for measuring observables multiple times.
+
+    Methods:
+        __init__(obs_list, v_c_reg_mapping=None): Initialize the MeasureMultiPauliSum module.
+        forward(qdev): Measure the sum of multiple Pauli operators.
+
+    Example:
+        obs_list = [
+            {
+                "wires": [0, 2, 3, 1],
+                "observables": ["x", "y", "z", "i"],
+                "coefficient": [1, 0.5, 0.4, 0.3]
+            },
+            {
+                "wires": [0, 2, 3, 1],
+                "observables": ["x", "y", "z", "i"],
+                "coefficient": [1, 0.5, 0.4, 0.3]
+            },
+        ]
+        measure_module = MeasureMultiPauliSum(obs_list)
+        result = measure_module(qdev)
     """
 
     def __init__(self, obs_list, v_c_reg_mapping=None):
+        """
+        Initialize the MeasureMultiPauliSum module.
+
+        Args:
+            obs_list (List[Dict]): The list of dictionaries specifying the observables, wires, and coefficients.
+            v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+        """
+        
         super().__init__()
         self.obs_list = obs_list
         self.v_c_reg_mapping = v_c_reg_mapping
@@ -418,27 +594,65 @@ def __init__(self, obs_list, v_c_reg_mapping=None):
             obs_list=obs_list, v_c_reg_mapping=v_c_reg_mapping
         )
 
-    def forward(self, qdev: tq.QuantumDevice):
+    def forward(self, qdev: tq.QuantumDevice) -> torch.Tensor:
+        """
+        Measure the sum of multi-qubit Pauli operators.
+
+        Args:
+            qdev (tq.QuantumDevice): The quantum device.
+
+        Returns:
+            torch.Tensor: The measurement results.
+        """
+        
         res_all = self.measure_multiple_times(qdev)
 
         return res_all.sum(-1)
 
 
 class MeasureMultiQubitPauliSum(tq.QuantumModule):
-    """obs list:
-    list of dict: example
-    [{'coefficient': [0.5, 0.2]},
-    {'wires': [0, 2, 3, 1],
-    'observables': ['x', 'y', 'z', 'i'],
-    },
-    {'wires': [0, 2, 3, 1],
-    'observables': ['y', 'x', 'z', 'i'],
-    },
-    ]
-    Measures 0.5 * <x y z i> + 0.2 * <y x z i>
+    """Measure a multi-qubit Pauli sum.
+
+    Attributes:
+        obs_list (List[Dict]): The list of observables and coefficients to measure.
+        v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+        measure_multiple_times (MeasureMultipleTimes): A module for measuring observables multiple times.
+
+    Methods:
+        __init__(obs_list, v_c_reg_mapping=None): Initialize the MeasureMultiQubitPauliSum module.
+        forward(qdev): Compute the expectation value of the multi-qubit Pauli sum.
+    
+    Examples:
+        obs_list = [
+            {
+                'coefficient': [0.5, 0.2],
+            },
+            {
+                'wires': [0, 2, 3, 1],
+                'observables': ['x', 'y', 'z', 'i'],
+            },
+            {
+                'wires': [0, 2, 3, 1],
+                'observables': ['y', 'x', 'z', 'i'],
+            }
+        ]
+
+        # Measures 0.5 * <x y z i> + 0.2 * <y x z i>
+
+        pauliSum = MeasureMultiQubitPauliSum(obs_list)
+        qdev = tq.QuantumDevice(n_wires=4)
+        result = pauliSum(qdev)
+        print(result)
     """
 
     def __init__(self, obs_list, v_c_reg_mapping=None):
+        """Initialize the MeasureMultiQubitPauliSum module.
+
+        Args:
+            obs_list (List[Dict]): The list of observables and coefficients to measure.
+            v_c_reg_mapping (Optional[Dict[int, int]]): The mapping of classical register indices to virtual qubit indices.
+        """
+        
         super().__init__()
         self.obs_list = obs_list
         self.v_c_reg_mapping = v_c_reg_mapping
@@ -447,6 +661,15 @@ def __init__(self, obs_list, v_c_reg_mapping=None):
         )
 
     def forward(self, qdev: tq.QuantumDevice):
+        """Compute the expectation value of the multi-qubit Pauli sum.
+
+        Args:
+            qdev (tq.QuantumDevice): The quantum device.
+
+        Returns:
+            torch.Tensor: The expectation value of the multi-qubit Pauli sum.
+        """
+        
         res_all = self.measure_multiple_times(qdev)
         return (res_all * self.obs_list[0]["coefficient"]).sum(-1)
 
@@ -469,5 +692,3 @@ def forward(self, qdev: tq.QuantumDevice):
     expval = expval_joint_sampling(qdev, 'II', 100000)
     expval_ana = expval_joint_analytical(qdev, 'II')
     print(expval, expval_ana)
-
-
diff --git a/torchquantum/module.py b/torchquantum/module.py
index 4a97dd32..8c57d291 100644
--- a/torchquantum/module.py
+++ b/torchquantum/module.py
@@ -12,7 +12,42 @@
 
 
 class QuantumModule(nn.Module):
+    """Module for quantum computations.
+    
+    Attributes:
+        static_mode (bool): Indicates whether the module is in static mode.
+        graph (tq.QuantumGraph): The quantum graph representation.
+        parent_graph (tq.QuantumGraph): The parent quantum graph.
+        is_graph_top (bool): Indicates whether the module is the top level of the graph.
+        unitary (torch.Tensor): The unitary matrix.
+        wires (List[int]): The wires.
+        n_wires (int): The number of wires.
+        q_device (tq.QuantumDevice): The quantum device.
+        device (torch.device): The device (CPU or GPU).
+        wires_per_block (int): The number of wires per block for static tensor network simulation.
+        qiskit_processor: The Qiskit processor.
+        noise_model_tq: The noise model.
+        Operator_list (tq.QuantumModuleList): The list of quantum operators.
+    
+    Methods:
+        __init__(self):
+            Initialize the QuantumModule.
+        load_op_history(self, op_history):
+            Load the operation history.
+        from_op_history(cls, op_history):
+            Create a QuantumModule from the operation history.
+    """
+    
     def __init__(self) -> None:
+        """Initialize the QuantumModule.
+        
+        Returns:
+            None.
+            
+        Examples:
+            >>> qmodule = QuantumModule()
+        """
+        
         super().__init__()
         self.static_mode = False
         self.graph = None
@@ -31,16 +66,27 @@ def __init__(self) -> None:
         self.Operator_list = None
     
     def load_op_history(self, op_history):
-        """load operation history
-            {
-                "name": name,  # type: ignore
-                "wires": np.array(wires).squeeze().tolist(),
-                "params": params.squeeze().detach().cpu().numpy().tolist() if params is not None else None,
-                "inverse": inverse,
-                "trainable": params.requires_grad if params is not None else False,
-            }
+        """Load the operation history.
+        
+        Args:
+            op_history (list of dict): A list of operations in function dict format.
 
+        Returns:
+            None.
+            
+        Examples:
+            >>> op_history = [
+            ...     {
+            ...         "name": name,   # type: ignore
+            ...         "wires": np.array(wires).squeeze().tolist(),
+            ...         "params": params.squeeze().detach().cpu().numpy().tolist() if params is not None else None,
+            ...         "inverse": inverse,
+            ...         "trainable": params.requires_grad if params is not None else False,
+            ...     }
+            ... ]
+            >>> qmodule.load_op_history(op_history)
         """
+
         Operator_list = []
         for op in op_history:
             Oper = tq.op_name_dict[op["name"]]
@@ -64,32 +110,115 @@ def load_op_history(self, op_history):
 
     @classmethod
     def from_op_history(cls, op_history):
-        """Create a QuantumModule from op_history
+        """Create a QuantumModule from the operation history.
+
         Args:
-            op_history (list): A list of op in function dict format
+            op_history (list): A list of operations in function dict format.
+
+        Returns:
+            QuantumModule: The created QuantumModule.
+        
+        Examples:
+            >>> op_history = [
+            ...     {
+            ...         "name": name,   # type: ignore
+            ...         "wires": np.array(wires).squeeze().tolist(),
+            ...         "params": params.squeeze().detach().cpu().numpy().tolist() if params is not None else None,
+            ...         "inverse": inverse,
+            ...         "trainable": params.requires_grad if params is not None else False,
+            ...     }
+            ... ]
+            >>> qmodule = QuantumModule.from_op_history(op_history)
         """
+        
         qmodule = cls()
         qmodule.load_op_history(op_history)
         qmodule.forward = qmodule.forward_Operators_list
         return qmodule
 
     def forward_Operators_list(self, qdev):
+        """Forward the list of quantum operators.
+
+        Args:
+            qdev (tq.QuantumDevice): The quantum device.
+        
+        Returns:
+            None.
+
+        Examples:
+            >>> qdev = tq.QuantumDevice(n_wires=2)
+            >>> qmodule.forward_Operators_list(qdev)
+        """
+        
         assert self.Operator_list is not None, "Operator_list should not contain nothing"
         for Oper in self.Operator_list:
             Oper(qdev)
 
     def set_noise_model_tq(self, noise_model_tq):
+        """Set the noise model for the QuantumModule and its sub-modules.
+
+        Args:
+            noise_model_tq: The noise model.
+        
+        Returns:
+            None.
+            
+        Examples:
+            >>> noise_model_tq = ...
+            >>> qmodule.set_noise_model_tq(noise_model_tq)
+        """
+        
         for module in self.modules():
             module.noise_model_tq = noise_model_tq
 
     def set_qiskit_processor(self, processor):
+        """Set the Qiskit processor for the QuantumModule and its sub-modules.
+
+        Args:
+            processor: The Qiskit processor.
+
+        Returns:
+            None.
+
+        Examples:
+            >>> processor = ...
+            >>> qmodule.set_qiskit_processor(processor)
+        """
+        
         for module in self.modules():
             module.qiskit_processor = processor
 
     def set_wires_per_block(self, wires_per_block):
+        """Set the number of wires per block for static tensor network simulation.
+
+        Args:
+            wires_per_block (int): The number of wires per block.
+
+        Returns:
+            None.
+
+        Examples:
+            >>> qmodule.set_wires_per_block(3)
+        """
+        
         self.wires_per_block = wires_per_block
 
     def static_on(self, is_graph_top=True, wires_per_block=3):
+        """Enable static mode for the QuantumModule.
+
+        Args:
+            is_graph_top (bool): Indicates whether the module is the top level of the graph.
+                Defaults to True.
+            wires_per_block (int): The number of wires per block.
+                Defaults to 3.
+
+        Returns:
+            None.
+
+        Examples:
+            >>> qmodule.static_on(wires_per_block=5)
+        """
+        
         self.wires_per_block = wires_per_block
         # register graph of itself and parent
         self.static_mode = True
@@ -108,6 +237,15 @@ def static_on(self, is_graph_top=True, wires_per_block=3):
                 module.static_on(is_graph_top=False)
 
     def static_off(self):
+        """Disable static mode for the QuantumModule.
+
+        Returns:
+            None.
+
+        Examples:
+            >>> qmodule.static_off()
+        """
+        
         self.static_mode = False
         self.graph = None
         for module in self.children():
@@ -115,12 +253,34 @@ def static_off(self):
                 module.static_off()
 
     def set_graph_build_finish(self):
+        """Set the graph build finish flag for the QuantumModule and its sub-modules.
+        
+        Returns:
+            None.        
+
+        Examples:
+            >>> qmodule.set_graph_build_finish()
+        """
+        
         self.graph.is_list_finish = True
         for module in self.graph.module_list:
             if not isinstance(module, tq.QuantumDevice):
                 module.set_graph_build_finish()
 
     def static_forward(self, q_device: tq.QuantumDevice):
+        """Static forward pass of the QuantumModule with the given quantum device.
+
+        Args:
+            q_device (tq.QuantumDevice): The quantum device.
+        
+        Returns:
+            None.
+            
+        Example:
+            >>> q_device = tq.QuantumDevice(n_wires=2)
+            >>> qmodule.static_forward(q_device)
+        """
+        
         self.q_device = q_device
         self.device = q_device.states.device
         self.graph.q_device = q_device
@@ -140,6 +300,21 @@ def static_forward(self, q_device: tq.QuantumDevice):
     #         return "QuantumModule"
 
     def get_unitary(self, q_device: tq.QuantumDevice, x=None):
+        """Compute the unitary matrix for the QuantumModule with the given quantum device and input.
+
+        Args:
+            q_device (tq.QuantumDevice): The quantum device.
+            x (Optional): The input.
+                Defaults to None.
+
+        Returns:
+            torch.Tensor: The unitary matrix.
+
+        Example:
+            >>> q_device = tq.QuantumDevice(n_wires=2)
+            >>> unitary = qmodule.get_unitary(q_device)
+        """
+        
         original_wires_per_block = self.wires_per_block
         original_static_mode = self.static_mode
         self.static_off()
@@ -170,17 +345,27 @@ def get_unitary(self, q_device: tq.QuantumDevice, x=None):
 
 
 class QuantumModuleList(nn.ModuleList, QuantumModule, metaclass=ABCMeta):
+    """A list-based container for QuantumModules."""
+    
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
 
 class QuantumModuleDict(nn.ModuleDict, QuantumModule, metaclass=ABCMeta):
+    """A dictionary-based container for QuantumModules."""
+    
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
 
 
 def test():
+    """Test function.
+    
+    Returns:
+        None.
+    """
+    
     pass
 
 
diff --git a/torchquantum/n_local/__init__.py b/torchquantum/n_local/__init__.py
new file mode 100644
index 00000000..e090cc52
--- /dev/null
+++ b/torchquantum/n_local/__init__.py
@@ -0,0 +1,14 @@
+"""The circuit library module containing N-local circuits."""
+
+from .n_local import NLocal
+from .two_local import TwoLocal
+from .real_amplitudes import RealAmplitudes
+from .efficient_su2 import EfficientSU2
+
+
+__all__ = [
+    "NLocal",
+    "TwoLocal",
+    "RealAmplitudes",
+    "EfficientSU2",
+]
diff --git a/torchquantum/n_local/efficient_su2.py b/torchquantum/n_local/efficient_su2.py
new file mode 100644
index 00000000..5374d14d
--- /dev/null
+++ b/torchquantum/n_local/efficient_su2.py
@@ -0,0 +1,98 @@
+
+#The efficient SU2 2-local circuit
+
+from typing import Union, Optional, List, Tuple, Callable, Any
+from numpy import pi
+
+from torchquantum.devices import QuantumDevice
+from torchquantum.operators import RY, RZ, CNOT
+from .two_local import TwoLocal
+
+class EfficientSU2(TwoLocal):
+
+
+    r"""The hardware efficient SU(2) 2-local circuit.
+
+    The ``EfficientSU2`` circuit consists of layers of single qubit operations spanned by SU(2)
+    and :math:`CX` entanglements. This is a heuristic pattern that can be used to prepare trial wave
+    functions for variational quantum algorithms or classification circuit for machine learning.
+
+    SU(2) stands for special unitary group of degree 2, its elements are :math:`2 \times 2`
+    unitary matrices with determinant 1, such as the Pauli rotation gates.
+
+    On 3 qubits and using the Pauli :math:`Y` and :math:`Z` su2_gates as single qubit gates, the
+    hardware efficient SU(2) circuit is represented by:
+    """
+
+    def __init__(
+        self,
+        num_qubits: Optional[int] = None,
+        su2_gates: Optional[
+            Union[
+                str,
+                type,
+                QuantumDevice,
+                List[Union[str, type,QuantumDevice]],
+            ]
+        ] = None,
+        entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "reverse_linear",
+        reps: int = 3,
+        skip_unentangled_qubits: bool = False,
+        skip_final_rotation_layer: bool = False,
+        parameter_prefix: str = "θ",
+        insert_barriers: bool = False,
+        initial_state: Optional[Any] = None,
+        name: str = "EfficientSU2",
+    ) -> None:
+        """Create a new EfficientSU2 2-local circuit.
+
+        Args:
+            num_qubits: The number of qubits of the EfficientSU2 circuit.
+            reps: Specifies how often the structure of a rotation layer followed by an entanglement
+                layer is repeated.
+            su2_gates: The SU(2) single qubit gates to apply in single qubit gate layers.
+                If only one gate is provided, the same gate is applied to each qubit.
+                If a list of gates is provided, all gates are applied to each qubit in the provided
+                order.
+            entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear'
+                , 'reverse_linear', 'circular' or 'sca'), a list of integer-pairs specifying the indices
+                of qubits entangled with one another, or a callable returning such a list provided with
+                the index of the entanglement layer.
+                Default to 'reverse_linear' entanglement.
+                Note that 'reverse_linear' entanglement provides the same unitary as 'full'
+                with fewer entangling gates.
+            initial_state: A `QuantumDevice` object to prepend to the circuit.
+            skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits
+                that are entangled with another qubit. If False, the single qubit gates are applied
+                to each qubit in the Ansatz. Defaults to False.
+            skip_final_rotation_layer: If False, a rotation layer is added at the end of the
+                ansatz. If True, no rotation layer is added.
+            parameter_prefix: The parameterized gates require a parameter to be defined
+            insert_barriers: If True, barriers are inserted in between each layer. If False,
+                no barriers are inserted.
+
+        """
+        if su2_gates is None:
+            su2_gates = [RY, RZ]
+        super().__init__(
+            num_qubits=num_qubits,
+            rotation_blocks=su2_gates,
+            entanglement_blocks=CNOT    ,
+            entanglement=entanglement,
+            reps=reps,
+            skip_unentangled_qubits=skip_unentangled_qubits,
+            skip_final_rotation_layer=skip_final_rotation_layer,
+            parameter_prefix=parameter_prefix,
+            insert_barriers=insert_barriers,
+            initial_state=initial_state,
+            name=name,
+        )
+
+    @property
+    def parameter_bounds(self) -> List[Tuple[float, float]]:
+        """Return the parameter bounds.
+
+        Returns:
+            The parameter bounds.
+        """
+        return self.num_parameters * [(-pi, pi)]
diff --git a/torchquantum/n_local/n_local.py b/torchquantum/n_local/n_local.py
new file mode 100644
index 00000000..e376c59b
--- /dev/null
+++ b/torchquantum/n_local/n_local.py
@@ -0,0 +1,1020 @@
+
+"""The n-local circuit class."""
+
+from __future__ import annotations
+
+import typing
+from typing import Union, Optional, Any, Sequence, Callable, Mapping
+from itertools import combinations
+
+#Lots of import changes required
+
+import numpy
+from qiskit.circuit.quantumcircuit import QuantumCircuit
+from qiskit.circuit.quantumregister import QuantumRegister
+from qiskit.circuit import Instruction, Parameter, ParameterVector, ParameterExpression
+from qiskit.exceptions import QiskitError
+
+class NLocal(BlueprintCircuit):
+    """The n-local circuit class.
+
+    The structure of the n-local circuit are alternating rotation and entanglement layers.
+    In both layers, parameterized circuit-blocks act on the circuit in a defined way.
+    In the rotation layer, the blocks are applied stacked on top of each other, while in the
+    entanglement layer according to the ``entanglement`` strategy.
+    The circuit blocks can have arbitrary sizes (smaller equal to the number of qubits in the
+    circuit). Each layer is repeated ``reps`` times, and by default a final rotation layer is
+    appended.
+
+    For instance, a rotation block on 2 qubits and an entanglement block on 4 qubits using
+    ``'linear'`` entanglement yields the following circuit.
+
+    .. parsed-literal::
+
+        ┌──────┐ ░ ┌──────┐                      ░ ┌──────┐
+        ┤0     ├─░─┤0     ├──────────────── ... ─░─┤0     ├
+        │  Rot │ ░ │      │┌──────┐              ░ │  Rot │
+        ┤1     ├─░─┤1     ├┤0     ├──────── ... ─░─┤1     ├
+        ├──────┤ ░ │  Ent ││      │┌──────┐      ░ ├──────┤
+        ┤0     ├─░─┤2     ├┤1     ├┤0     ├ ... ─░─┤0     ├
+        │  Rot │ ░ │      ││  Ent ││      │      ░ │  Rot │
+        ┤1     ├─░─┤3     ├┤2     ├┤1     ├ ... ─░─┤1     ├
+        ├──────┤ ░ └──────┘│      ││  Ent │      ░ ├──────┤
+        ┤0     ├─░─────────┤3     ├┤2     ├ ... ─░─┤0     ├
+        │  Rot │ ░         └──────┘│      │      ░ │  Rot │
+        ┤1     ├─░─────────────────┤3     ├ ... ─░─┤1     ├
+        └──────┘ ░                 └──────┘      ░ └──────┘
+
+        |                                 |
+        +---------------------------------+
+               repeated reps times
+
+    If specified, barriers can be inserted in between every block.
+    If an initial state object is provided, it is added in front of the NLocal.
+    """
+
+    def __init__(
+        self,
+        num_qubits: int | None = None,
+        rotation_blocks: QuantumCircuit
+        | list[QuantumCircuit]
+        | qiskit.circuit.Instruction
+        | list[qiskit.circuit.Instruction]
+        | None = None,
+        entanglement_blocks: QuantumCircuit
+        | list[QuantumCircuit]
+        | qiskit.circuit.Instruction
+        | list[qiskit.circuit.Instruction]
+        | None = None,
+        entanglement: list[int] | list[list[int]] | None = None,
+        reps: int = 1,
+        insert_barriers: bool = False,
+        parameter_prefix: str = "θ",
+        overwrite_block_parameters: bool | list[list[Parameter]] = True,
+        skip_final_rotation_layer: bool = False,
+        skip_unentangled_qubits: bool = False,
+        initial_state: QuantumCircuit | None = None,
+        name: str | None = "nlocal",
+    ) -> None:
+        """Create a new n-local circuit.
+
+        Args:
+            num_qubits: The number of qubits of the circuit.
+            rotation_blocks: The blocks used in the rotation layers. If multiple are passed,
+                these will be applied one after another (like new sub-layers).
+            entanglement_blocks: The blocks used in the entanglement layers. If multiple are passed,
+                these will be applied one after another. To use different entanglements for
+                the sub-layers, see :meth:`get_entangler_map`.
+            entanglement: The indices specifying on which qubits the input blocks act. If ``None``, the
+                entanglement blocks are applied at the top of the circuit.
+            reps: Specifies how often the rotation blocks and entanglement blocks are repeated.
+            insert_barriers: If ``True``, barriers are inserted in between each layer. If ``False``,
+                no barriers are inserted.
+            parameter_prefix: The prefix used if default parameters are generated.
+            overwrite_block_parameters: If the parameters in the added blocks should be overwritten.
+                If ``False``, the parameters in the blocks are not changed.
+            skip_final_rotation_layer: Whether a final rotation layer is added to the circuit.
+            skip_unentangled_qubits: If ``True``, the rotation gates act only on qubits that
+                are entangled. If ``False``, the rotation gates act on all qubits.
+            initial_state: A :class:`.QuantumCircuit` object which can be used to describe an initial
+                state prepended to the NLocal circuit.
+            name: The name of the circuit.
+
+        Examples:
+            TODO
+
+        Raises:
+            ValueError: If ``reps`` parameter is less than or equal to 0.
+            TypeError: If ``reps`` parameter is not an int value.
+        """
+        super().__init__(name=name)
+
+        self._num_qubits: int | None = None
+        self._insert_barriers = insert_barriers
+        self._reps = reps
+        self._entanglement_blocks: list[QuantumCircuit] = []
+        self._rotation_blocks: list[QuantumCircuit] = []
+        self._prepended_blocks: list[QuantumCircuit] = []
+        self._prepended_entanglement: list[list[list[int]] | str] = []
+        self._appended_blocks: list[QuantumCircuit] = []
+        self._appended_entanglement: list[list[list[int]] | str] = []
+        self._entanglement = None
+        self._entangler_maps = None
+        self._ordered_parameters: ParameterVector | list[Parameter] = ParameterVector(
+            name=parameter_prefix
+        )
+        self._overwrite_block_parameters = overwrite_block_parameters
+        self._skip_final_rotation_layer = skip_final_rotation_layer
+        self._skip_unentangled_qubits = skip_unentangled_qubits
+        self._initial_state: QuantumCircuit | None = None
+        self._initial_state_circuit: QuantumCircuit | None = None
+        self._bounds: list[tuple[float | None, float | None]] | None = None
+
+        if int(reps) != reps:
+            raise TypeError("The value of reps should be int")
+
+        if reps < 0:
+            raise ValueError("The value of reps should be larger than or equal to 0")
+
+        if num_qubits is not None:
+            self.num_qubits = num_qubits
+
+        if entanglement_blocks is not None:
+            self.entanglement_blocks = entanglement_blocks
+
+        if rotation_blocks is not None:
+            self.rotation_blocks = rotation_blocks
+
+        if entanglement is not None:
+            self.entanglement = entanglement
+
+        if initial_state is not None:
+            self.initial_state = initial_state
+
+    @property
+    def num_qubits(self) -> int:
+        """Returns the number of qubits in this circuit.
+
+        Returns:
+            The number of qubits.
+        """
+        return self._num_qubits if self._num_qubits is not None else 0
+
+    @num_qubits.setter
+    def num_qubits(self, num_qubits: int) -> None:
+        """Set the number of qubits for the n-local circuit.
+
+        Args:
+            The new number of qubits.
+        """
+        if self._num_qubits != num_qubits:
+            # invalidate the circuit
+            self._invalidate()
+            self._num_qubits = num_qubits
+            self.qregs = [QuantumRegister(num_qubits, name="q")]
+
+    def _convert_to_block(self, layer: Any) -> QuantumCircuit:
+        """Try to convert ``layer`` to a QuantumCircuit.
+
+        Args:
+            layer: The object to be converted to an NLocal block / Instruction.
+
+        Returns:
+            The layer converted to a circuit.
+
+        Raises:
+            TypeError: If the input cannot be converted to a circuit.
+        """
+        if isinstance(layer, QuantumCircuit):
+            return layer
+
+        if isinstance(layer, Instruction):
+            circuit = QuantumCircuit(layer.num_qubits)
+            circuit.append(layer, list(range(layer.num_qubits)))
+            return circuit
+
+        try:
+            circuit = QuantumCircuit(layer.num_qubits)
+            circuit.append(layer.to_instruction(), list(range(layer.num_qubits)))
+            return circuit
+        except AttributeError:
+            pass
+
+        raise TypeError(f"Adding a {type(layer)} to an NLocal is not supported.")
+
+    @property
+    def rotation_blocks(self) -> list[QuantumCircuit]:
+        """The blocks in the rotation layers.
+
+        Returns:
+            The blocks in the rotation layers.
+        """
+        return self._rotation_blocks
+
+    @rotation_blocks.setter
+    def rotation_blocks(
+        self, blocks: QuantumCircuit | list[QuantumCircuit] | Instruction | list[Instruction]
+    ) -> None:
+        """Set the blocks in the rotation layers.
+
+        Args:
+            blocks: The new blocks for the rotation layers.
+        """
+        # cannot check for the attribute ``'__len__'`` because a circuit also has this attribute
+        if not isinstance(blocks, (list, numpy.ndarray)):
+            blocks = [blocks]
+
+        self._invalidate()
+        self._rotation_blocks = [self._convert_to_block(block) for block in blocks]
+
+    @property
+    def entanglement_blocks(self) -> list[QuantumCircuit]:
+        """The blocks in the entanglement layers.
+
+        Returns:
+            The blocks in the entanglement layers.
+        """
+        return self._entanglement_blocks
+
+    @entanglement_blocks.setter
+    def entanglement_blocks(
+        self, blocks: QuantumCircuit | list[QuantumCircuit] | Instruction | list[Instruction]
+    ) -> None:
+        """Set the blocks in the entanglement layers.
+
+        Args:
+            blocks: The new blocks for the entanglement layers.
+        """
+        # cannot check for the attribute ``'__len__'`` because a circuit also has this attribute
+        if not isinstance(blocks, (list, numpy.ndarray)):
+            blocks = [blocks]
+
+        self._invalidate()
+        self._entanglement_blocks = [self._convert_to_block(block) for block in blocks]
+
+    @property
+    def entanglement(
+        self,
+    ) -> Union[
+        str,
+        list[str],
+        list[list[str]],
+        list[int],
+        list[list[int]],
+        list[list[list[int]]],
+        list[list[list[list[int]]]],
+        Callable[[int], str],
+        Callable[[int], list[list[int]]],
+    ]:
+        """Get the entanglement strategy.
+
+        Returns:
+            The entanglement strategy, see :meth:`get_entangler_map` for more detail on how the
+            format is interpreted.
+        """
+        return self._entanglement
+
+    @entanglement.setter
+    def entanglement(
+        self,
+        entanglement: Optional[
+            Union[
+                str,
+                list[str],
+                list[list[str]],
+                list[int],
+                list[list[int]],
+                list[list[list[int]]],
+                list[list[list[list[int]]]],
+                Callable[[int], str],
+                Callable[[int], list[list[int]]],
+            ]
+        ],
+    ) -> None:
+        """Set the entanglement strategy.
+
+        Args:
+            entanglement: The entanglement strategy. See :meth:`get_entangler_map` for more detail
+                on the supported formats.
+        """
+        self._invalidate()
+        self._entanglement = entanglement
+
+    @property
+    def num_layers(self) -> int:
+        """Return the number of layers in the n-local circuit.
+
+        Returns:
+            The number of layers in the circuit.
+        """
+        return 2 * self._reps + int(not self._skip_final_rotation_layer)
+
+    def _check_configuration(self, raise_on_failure: bool = True) -> bool:
+        """Check if the configuration of the NLocal class is valid.
+
+        Args:
+            raise_on_failure: Whether to raise on failure.
+
+        Returns:
+            True, if the configuration is valid and the circuit can be constructed. Otherwise
+            an ValueError is raised.
+
+        Raises:
+            ValueError: If the blocks are not set.
+            ValueError: If the number of repetitions is not set.
+            ValueError: If the qubit indices are not set.
+            ValueError: If the number of qubit indices does not match the number of blocks.
+            ValueError: If an index in the repetitions list exceeds the number of blocks.
+            ValueError: If the number of repetitions does not match the number of block-wise
+                parameters.
+            ValueError: If a specified qubit index is larger than the (manually set) number of
+                qubits.
+        """
+        valid = True
+        if self.num_qubits is None:
+            valid = False
+            if raise_on_failure:
+                raise ValueError("No number of qubits specified.")
+
+        # check no needed parameters are None
+        if self.entanglement_blocks is None and self.rotation_blocks is None:
+            valid = False
+            if raise_on_failure:
+                raise ValueError("The blocks are not set.")
+
+        return valid
+
+    @property
+    def ordered_parameters(self) -> list[Parameter]:
+        """The parameters used in the underlying circuit.
+
+        This includes float values and duplicates.
+
+        Examples:
+
+            >>> # prepare circuit ...
+            >>> print(nlocal)
+                 ┌───────┐┌──────────┐┌──────────┐┌──────────┐
+            q_0: ┤ Ry(1) ├┤ Ry(θ[1]) ├┤ Ry(θ[1]) ├┤ Ry(θ[3]) ├
+                 └───────┘└──────────┘└──────────┘└──────────┘
+            >>> nlocal.parameters
+            {Parameter(θ[1]), Parameter(θ[3])}
+            >>> nlocal.ordered_parameters
+            [1, Parameter(θ[1]), Parameter(θ[1]), Parameter(θ[3])]
+
+        Returns:
+            The parameters objects used in the circuit.
+        """
+        if isinstance(self._ordered_parameters, ParameterVector):
+            self._ordered_parameters.resize(self.num_parameters_settable)
+            return list(self._ordered_parameters)
+
+        return self._ordered_parameters
+
+    @ordered_parameters.setter
+    def ordered_parameters(self, parameters: ParameterVector | list[Parameter]) -> None:
+        """Set the parameters used in the underlying circuit.
+
+        Args:
+            The parameters to be used in the underlying circuit.
+
+        Raises:
+            ValueError: If the length of ordered parameters does not match the number of
+                parameters in the circuit and they are not a ``ParameterVector`` (which could
+                be resized to fit the number of parameters).
+        """
+        if (
+            not isinstance(parameters, ParameterVector)
+            and len(parameters) != self.num_parameters_settable
+        ):
+            raise ValueError(
+                "The length of ordered parameters must be equal to the number of "
+                "settable parameters in the circuit ({}), but is {}".format(
+                    self.num_parameters_settable, len(parameters)
+                )
+            )
+        self._ordered_parameters = parameters
+        self._invalidate()
+
+    @property
+    def insert_barriers(self) -> bool:
+        """If barriers are inserted in between the layers or not.
+
+        Returns:
+            ``True``, if barriers are inserted in between the layers, ``False`` if not.
+        """
+        return self._insert_barriers
+
+    @insert_barriers.setter
+    def insert_barriers(self, insert_barriers: bool) -> None:
+        """Specify whether barriers should be inserted in between the layers or not.
+
+        Args:
+            insert_barriers: If True, barriers are inserted, if False not.
+        """
+        # if insert_barriers changes, we have to invalidate the circuit definition,
+        # if it is the same as before we can leave the NLocal instance as it is
+        if insert_barriers is not self._insert_barriers:
+            self._invalidate()
+            self._insert_barriers = insert_barriers
+
+    def get_unentangled_qubits(self) -> set[int]:
+        """Get the indices of unentangled qubits in a set.
+
+        Returns:
+            The unentangled qubits.
+        """
+        entangled_qubits = set()
+        for i in range(self._reps):
+            for j, block in enumerate(self.entanglement_blocks):
+                entangler_map = self.get_entangler_map(i, j, block.num_qubits)
+                entangled_qubits.update([idx for indices in entangler_map for idx in indices])
+        unentangled_qubits = set(range(self.num_qubits)) - entangled_qubits
+
+        return unentangled_qubits
+
+    @property
+    def num_parameters_settable(self) -> int:
+        """The number of total parameters that can be set to distinct values.
+
+        This does not change when the parameters are bound or exchanged for same parameters,
+        and therefore is different from ``num_parameters`` which counts the number of unique
+        :class:`~qiskit.circuit.Parameter` objects currently in the circuit.
+
+        Returns:
+            The number of parameters originally available in the circuit.
+
+        Note:
+            This quantity does not require the circuit to be built yet.
+        """
+        num = 0
+
+        for i in range(self._reps):
+            for j, block in enumerate(self.entanglement_blocks):
+                entangler_map = self.get_entangler_map(i, j, block.num_qubits)
+                num += len(entangler_map) * len(get_parameters(block))
+
+        if self._skip_unentangled_qubits:
+            unentangled_qubits = self.get_unentangled_qubits()
+
+        num_rot = 0
+        for block in self.rotation_blocks:
+            block_indices = [
+                list(range(j * block.num_qubits, (j + 1) * block.num_qubits))
+                for j in range(self.num_qubits // block.num_qubits)
+            ]
+            if self._skip_unentangled_qubits:
+                block_indices = [
+                    indices
+                    for indices in block_indices
+                    if set(indices).isdisjoint(unentangled_qubits)
+                ]
+            num_rot += len(block_indices) * len(get_parameters(block))
+
+        num += num_rot * (self._reps + int(not self._skip_final_rotation_layer))
+
+        return num
+
+    @property
+    def reps(self) -> int:
+        """The number of times rotation and entanglement block are repeated.
+
+        Returns:
+            The number of repetitions.
+        """
+        return self._reps
+
+    @reps.setter
+    def reps(self, repetitions: int) -> None:
+        """Set the repetitions.
+
+        If the repetitions are `0`, only one rotation layer with no entanglement
+        layers is applied (unless ``self.skip_final_rotation_layer`` is set to ``True``).
+
+        Args:
+            repetitions: The new repetitions.
+
+        Raises:
+            ValueError: If reps setter has parameter repetitions < 0.
+        """
+        if repetitions < 0:
+            raise ValueError("The repetitions should be larger than or equal to 0")
+        if repetitions != self._reps:
+            self._invalidate()
+            self._reps = repetitions
+
+    def print_settings(self) -> str:
+        """Returns information about the setting.
+
+        Returns:
+            The class name and the attributes/parameters of the instance as ``str``.
+        """
+        ret = f"NLocal: {self.__class__.__name__}\n"
+        params = ""
+        for key, value in self.__dict__.items():
+            if key[0] == "_":
+                params += f"-- {key[1:]}: {value}\n"
+        ret += f"{params}"
+        return ret
+
+    @property
+    def preferred_init_points(self) -> list[float] | None:
+        """The initial points for the parameters. Can be stored as initial guess in optimization.
+
+        Returns:
+            The initial values for the parameters, or None, if none have been set.
+        """
+        return None
+
+    # pylint: disable=too-many-return-statements
+    def get_entangler_map(
+        self, rep_num: int, block_num: int, num_block_qubits: int
+    ) -> Sequence[Sequence[int]]:
+        """Get the entangler map for in the repetition ``rep_num`` and the block ``block_num``.
+
+        The entangler map for the current block is derived from the value of ``self.entanglement``.
+        Below the different cases are listed, where ``i`` and ``j`` denote the repetition number
+        and the block number, respectively, and ``n`` the number of qubits in the block.
+
+        =================================== ========================================================
+        entanglement type                   entangler map
+        =================================== ========================================================
+        ``None``                            ``[[0, ..., n - 1]]``
+        ``str`` (e.g ``'full'``)            the specified connectivity on ``n`` qubits
+        ``List[int]``                       [``entanglement``]
+        ``List[List[int]]``                 ``entanglement``
+        ``List[List[List[int]]]``           ``entanglement[i]``
+        ``List[List[List[List[int]]]]``     ``entanglement[i][j]``
+        ``List[str]``                       the connectivity specified in ``entanglement[i]``
+        ``List[List[str]]``                 the connectivity specified in ``entanglement[i][j]``
+        ``Callable[int, str]``              same as ``List[str]``
+        ``Callable[int, List[List[int]]]``  same as ``List[List[List[int]]]``
+        =================================== ========================================================
+
+
+        Note that all indices are to be taken modulo the length of the array they act on, i.e.
+        no out-of-bounds index error will be raised but we re-iterate from the beginning of the
+        list.
+
+        Args:
+            rep_num: The current repetition we are in.
+            block_num: The block number within the entanglement layers.
+            num_block_qubits: The number of qubits in the block.
+
+        Returns:
+            The entangler map for the current block in the current repetition.
+
+        Raises:
+            ValueError: If the value of ``entanglement`` could not be cast to a corresponding
+                entangler map.
+        """
+        i, j, n = rep_num, block_num, num_block_qubits
+        entanglement = self._entanglement
+
+        # entanglement is None
+        if entanglement is None:
+            return [list(range(n))]
+
+        # entanglement is callable
+        if callable(entanglement):
+            entanglement = entanglement(i)
+
+        # entanglement is str
+        if isinstance(entanglement, str):
+            return get_entangler_map(n, self.num_qubits, entanglement, offset=i)
+
+        # check if entanglement is list of something
+        if not isinstance(entanglement, (tuple, list)):
+            raise ValueError(f"Invalid value of entanglement: {entanglement}")
+        num_i = len(entanglement)
+
+        # entanglement is List[str]
+        if all(isinstance(en, str) for en in entanglement):
+            return get_entangler_map(n, self.num_qubits, entanglement[i % num_i], offset=i)
+
+        # entanglement is List[int]
+        if all(isinstance(en, (int, numpy.integer)) for en in entanglement):
+            return [[int(en) for en in entanglement]]
+
+        # check if entanglement is List[List]
+        if not all(isinstance(en, (tuple, list)) for en in entanglement):
+            raise ValueError(f"Invalid value of entanglement: {entanglement}")
+        num_j = len(entanglement[i % num_i])
+
+        # entanglement is List[List[str]]
+        if all(isinstance(e2, str) for en in entanglement for e2 in en):
+            return get_entangler_map(
+                n, self.num_qubits, entanglement[i % num_i][j % num_j], offset=i
+            )
+
+        # entanglement is List[List[int]]
+        if all(isinstance(e2, (int, numpy.int32, numpy.int64)) for en in entanglement for e2 in en):
+            for ind, en in enumerate(entanglement):
+                entanglement[ind] = tuple(map(int, en))
+            return entanglement
+
+        # check if entanglement is List[List[List]]
+        if not all(isinstance(e2, (tuple, list)) for en in entanglement for e2 in en):
+            raise ValueError(f"Invalid value of entanglement: {entanglement}")
+
+        # entanglement is List[List[List[int]]]
+        if all(
+            isinstance(e3, (int, numpy.int32, numpy.int64))
+            for en in entanglement
+            for e2 in en
+            for e3 in e2
+        ):
+            for en in entanglement:
+                for ind, e2 in enumerate(en):
+                    en[ind] = tuple(map(int, e2))
+            return entanglement[i % num_i]
+
+        # check if entanglement is List[List[List[List]]]
+        if not all(isinstance(e3, (tuple, list)) for en in entanglement for e2 in en for e3 in e2):
+            raise ValueError(f"Invalid value of entanglement: {entanglement}")
+
+        # entanglement is List[List[List[List[int]]]]
+        if all(
+            isinstance(e4, (int, numpy.int32, numpy.int64))
+            for en in entanglement
+            for e2 in en
+            for e3 in e2
+            for e4 in e3
+        ):
+            for en in entanglement:
+                for e2 in en:
+                    for ind, e3 in enumerate(e2):
+                        e2[ind] = tuple(map(int, e3))
+            return entanglement[i % num_i][j % num_j]
+
+        raise ValueError(f"Invalid value of entanglement: {entanglement}")
+
+    @property
+    def initial_state(self) -> QuantumCircuit:
+        """Return the initial state that is added in front of the n-local circuit.
+
+        Returns:
+            The initial state.
+        """
+        return self._initial_state
+
+    @initial_state.setter
+    def initial_state(self, initial_state: QuantumCircuit) -> None:
+        """Set the initial state.
+
+        Args:
+            initial_state: The new initial state.
+
+        Raises:
+            ValueError: If the number of qubits has been set before and the initial state
+                does not match the number of qubits.
+        """
+        self._initial_state = initial_state
+        self._invalidate()
+
+    @property
+    def parameter_bounds(self) -> list[tuple[float, float]] | None:
+        """The parameter bounds for the unbound parameters in the circuit.
+
+        Returns:
+            A list of pairs indicating the bounds, as (lower, upper). None indicates an unbounded
+            parameter in the corresponding direction. If ``None`` is returned, problem is fully
+            unbounded.
+        """
+        if not self._is_built:
+            self._build()
+        return self._bounds
+
+    @parameter_bounds.setter
+    def parameter_bounds(self, bounds: list[tuple[float, float]]) -> None:
+        """Set the parameter bounds.
+
+        Args:
+            bounds: The new parameter bounds.
+        """
+        self._bounds = bounds
+
+    def add_layer(
+        self,
+        other: Union["NLocal", qiskit.circuit.Instruction, QuantumCircuit],
+        entanglement: list[int] | str | list[list[int]] | None = None,
+        front: bool = False,
+    ) -> "NLocal":
+        """Append another layer to the NLocal.
+
+        Args:
+            other: The layer to compose, can be another NLocal, an Instruction or Gate,
+                or a QuantumCircuit.
+            entanglement: The entanglement or qubit indices.
+            front: If True, ``other`` is appended to the front, else to the back.
+
+        Returns:
+            self, such that chained composes are possible.
+
+        Raises:
+            TypeError: If `other` is not compatible, i.e. is no Instruction and does not have a
+                `to_instruction` method.
+        """
+        block = self._convert_to_block(other)
+
+        if entanglement is None:
+            entanglement = [list(range(block.num_qubits))]
+        elif isinstance(entanglement, list) and not isinstance(entanglement[0], list):
+            entanglement = [entanglement]
+        if front:
+            self._prepended_blocks += [block]
+            self._prepended_entanglement += [entanglement]
+        else:
+            self._appended_blocks += [block]
+            self._appended_entanglement += [entanglement]
+
+        if isinstance(entanglement, list):
+            num_qubits = 1 + max(max(indices) for indices in entanglement)
+            if num_qubits > self.num_qubits:
+                self._invalidate()  # rebuild circuit
+                self.num_qubits = num_qubits
+
+        # modify the circuit accordingly
+        if front is False and self._is_built:
+            if self._insert_barriers and len(self.data) > 0:
+                self.barrier()
+
+            if isinstance(entanglement, str):
+                entangler_map: Sequence[Sequence[int]] = get_entangler_map(
+                    block.num_qubits, self.num_qubits, entanglement
+                )
+            else:
+                entangler_map = entanglement
+
+            layer = QuantumCircuit(self.num_qubits)
+            for i in entangler_map:
+                params = self.ordered_parameters[-len(get_parameters(block)) :]
+                parameterized_block = self._parameterize_block(block, params=params)
+                layer.compose(parameterized_block, i, inplace=True)
+
+            self.compose(layer, inplace=True)
+        else:
+            # cannot prepend a block currently, just rebuild
+            self._invalidate()
+
+        return self
+
+    def assign_parameters(
+        self,
+        parameters: Mapping[Parameter, ParameterExpression | float]
+        | Sequence[ParameterExpression | float],
+        inplace: bool = False,
+    ) -> QuantumCircuit | None:
+        """Assign parameters to the n-local circuit.
+
+        This method also supports passing a list instead of a dictionary. If a list
+        is passed, the list must have the same length as the number of unbound parameters in
+        the circuit. The parameters are assigned in the order of the parameters in
+        :meth:`ordered_parameters`.
+
+        Returns:
+            A copy of the NLocal circuit with the specified parameters.
+
+        Raises:
+            AttributeError: If the parameters are given as list and do not match the number
+                of parameters.
+        """
+        if parameters is None or len(parameters) == 0:
+            return self
+
+        if not self._is_built:
+            self._build()
+
+        return super().assign_parameters(parameters, inplace=inplace)
+
+    def _parameterize_block(
+        self, block, param_iter=None, rep_num=None, block_num=None, indices=None, params=None
+    ):
+        """Convert ``block`` to a circuit of correct width and parameterized using the iterator."""
+        if self._overwrite_block_parameters:
+            # check if special parameters should be used
+            # pylint: disable=assignment-from-none
+            if params is None:
+                params = self._parameter_generator(rep_num, block_num, indices)
+            if params is None:
+                params = [next(param_iter) for _ in range(len(get_parameters(block)))]
+
+            update = dict(zip(block.parameters, params))
+            return block.assign_parameters(update)
+
+        return block.copy()
+
+    def _build_rotation_layer(self, circuit, param_iter, i):
+        """Build a rotation layer."""
+        # if the unentangled qubits are skipped, compute the set of qubits that are not entangled
+        if self._skip_unentangled_qubits:
+            unentangled_qubits = self.get_unentangled_qubits()
+
+        # iterate over all rotation blocks
+        for j, block in enumerate(self.rotation_blocks):
+            # create a new layer
+            layer = QuantumCircuit(*self.qregs)
+
+            # we apply the rotation gates stacked on top of each other, i.e.
+            # if we have 4 qubits and a rotation block of width 2, we apply two instances
+            block_indices = [
+                list(range(k * block.num_qubits, (k + 1) * block.num_qubits))
+                for k in range(self.num_qubits // block.num_qubits)
+            ]
+
+            # if unentangled qubits should not be acted on, remove all operations that
+            # touch an unentangled qubit
+            if self._skip_unentangled_qubits:
+                block_indices = [
+                    indices
+                    for indices in block_indices
+                    if set(indices).isdisjoint(unentangled_qubits)
+                ]
+
+            # apply the operations in the layer
+            for indices in block_indices:
+                parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
+                layer.compose(parameterized_block, indices, inplace=True)
+
+            # add the layer to the circuit
+            circuit.compose(layer, inplace=True)
+
+    def _build_entanglement_layer(self, circuit, param_iter, i):
+        """Build an entanglement layer."""
+        # iterate over all entanglement blocks
+        for j, block in enumerate(self.entanglement_blocks):
+            # create a new layer and get the entangler map for this block
+            layer = QuantumCircuit(*self.qregs)
+            entangler_map = self.get_entangler_map(i, j, block.num_qubits)
+
+            # apply the operations in the layer
+            for indices in entangler_map:
+                parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
+                layer.compose(parameterized_block, indices, inplace=True)
+
+            # add the layer to the circuit
+            circuit.compose(layer, inplace=True)
+
+    def _build_additional_layers(self, circuit, which):
+        if which == "appended":
+            blocks = self._appended_blocks
+            entanglements = self._appended_entanglement
+        elif which == "prepended":
+            blocks = reversed(self._prepended_blocks)
+            entanglements = reversed(self._prepended_entanglement)
+        else:
+            raise ValueError("`which` must be either `appended` or `prepended`.")
+
+        for block, ent in zip(blocks, entanglements):
+            layer = QuantumCircuit(*self.qregs)
+            if isinstance(ent, str):
+                ent = get_entangler_map(block.num_qubits, self.num_qubits, ent)
+            for indices in ent:
+                layer.compose(block, indices, inplace=True)
+
+            circuit.compose(layer, inplace=True)
+
+    def _build(self) -> None:
+        """If not already built, build the circuit."""
+        if self._is_built:
+            return
+
+        super()._build()
+
+        if self.num_qubits == 0:
+            return
+
+        circuit = QuantumCircuit(*self.qregs, name=self.name)
+
+        # use the initial state as starting circuit, if it is set
+        if self.initial_state:
+            circuit.compose(self.initial_state.copy(), inplace=True)
+
+        param_iter = iter(self.ordered_parameters)
+
+        # build the prepended layers
+        self._build_additional_layers(circuit, "prepended")
+
+        # main loop to build the entanglement and rotation layers
+        for i in range(self.reps):
+            # insert barrier if specified and there is a preceding layer
+            if self._insert_barriers and (i > 0 or len(self._prepended_blocks) > 0):
+                circuit.barrier()
+
+            # build the rotation layer
+            self._build_rotation_layer(circuit, param_iter, i)
+
+            # barrier in between rotation and entanglement layer
+            if self._insert_barriers and len(self._rotation_blocks) > 0:
+                circuit.barrier()
+
+            # build the entanglement layer
+            self._build_entanglement_layer(circuit, param_iter, i)
+
+        # add the final rotation layer
+        if not self._skip_final_rotation_layer:
+            if self.insert_barriers and self.reps > 0:
+                circuit.barrier()
+            self._build_rotation_layer(circuit, param_iter, self.reps)
+
+        # add the appended layers
+        self._build_additional_layers(circuit, "appended")
+
+        # cast global phase to float if it has no free parameters
+        if isinstance(circuit.global_phase, ParameterExpression):
+            try:
+                circuit.global_phase = float(circuit.global_phase)
+            except TypeError:
+                # expression contains free parameters
+                pass
+
+        try:
+            block = circuit.to_gate()
+        except QiskitError:
+            block = circuit.to_instruction()
+
+        self.append(block, self.qubits)
+
+    # pylint: disable=unused-argument
+    def _parameter_generator(self, rep: int, block: int, indices: list[int]) -> Parameter | None:
+        """If certain blocks should use certain parameters this method can be overriden."""
+        return None
+
+
+def get_parameters(block: QuantumCircuit | Instruction) -> list[Parameter]:
+    """Return the list of Parameters objects inside a circuit or instruction.
+
+    This is required since, in a standard gate the parameters are not necessarily Parameter
+    objects (e.g. U3Gate(0.1, 0.2, 0.3).params == [0.1, 0.2, 0.3]) and instructions and
+    circuits do not have the same interface for parameters.
+    """
+    if isinstance(block, QuantumCircuit):
+        return list(block.parameters)
+    else:
+        return [p for p in block.params if isinstance(p, ParameterExpression)]
+
+
+def get_entangler_map(
+    num_block_qubits: int, num_circuit_qubits: int, entanglement: str, offset: int = 0
+) -> Sequence[tuple[int, ...]]:
+    """Get an entangler map for an arbitrary number of qubits.
+
+    Args:
+        num_block_qubits: The number of qubits of the entangling block.
+        num_circuit_qubits: The number of qubits of the circuit.
+        entanglement: The entanglement strategy.
+        offset: The block offset, can be used if the entanglements differ per block.
+            See mode ``sca`` for instance.
+
+    Returns:
+        The entangler map using mode ``entanglement`` to scatter a block of ``num_block_qubits``
+        qubits on ``num_circuit_qubits`` qubits.
+
+    Raises:
+        ValueError: If the entanglement mode ist not supported.
+    """
+    n, m = num_circuit_qubits, num_block_qubits
+    if m > n:
+        raise ValueError(
+            "The number of block qubits must be smaller or equal to the number of "
+            "qubits in the circuit."
+        )
+
+    if entanglement == "pairwise" and num_block_qubits > 2:
+        raise ValueError("Pairwise entanglement is not defined for blocks with more than 2 qubits.")
+
+    if entanglement == "full":
+        return list(combinations(list(range(n)), m))
+    elif entanglement == "reverse_linear":
+        # reverse linear connectivity. In the case of m=2 and the entanglement_block='cx'
+        # then it's equivalent to 'full' entanglement
+        reverse = [tuple(range(n - i - m, n - i)) for i in range(n - m + 1)]
+        return reverse
+    elif entanglement in ["linear", "circular", "sca", "pairwise"]:
+        linear = [tuple(range(i, i + m)) for i in range(n - m + 1)]
+        # if the number of block qubits is 1, we don't have to add the 'circular' part
+        if entanglement == "linear" or m == 1:
+            return linear
+
+        if entanglement == "pairwise":
+            return linear[::2] + linear[1::2]
+
+        # circular equals linear plus top-bottom entanglement (if there's space for it)
+        if n > m:
+            circular = [tuple(range(n - m + 1, n)) + (0,)] + linear
+        else:
+            circular = linear
+        if entanglement == "circular":
+            return circular
+
+        # sca is circular plus shift and reverse
+        shifted = circular[-offset:] + circular[:-offset]
+        if offset % 2 == 1:  # if odd, reverse the qubit indices
+            sca = [ind[::-1] for ind in shifted]
+        else:
+            sca = shifted
+
+        return sca
+
+    else:
+        raise ValueError(f"Unsupported entanglement type: {entanglement}")
diff --git a/torchquantum/n_local/real_amplitudes.py b/torchquantum/n_local/real_amplitudes.py
new file mode 100644
index 00000000..319ac75d
--- /dev/null
+++ b/torchquantum/n_local/real_amplitudes.py
@@ -0,0 +1,89 @@
+
+from typing import Union, Optional, List, Tuple, Callable, Any
+import numpy as np
+
+from torchquantum.operators import RY, CNOT     
+from .two_local import TwoLocal
+
+class RealAmplitudes(TwoLocal):
+    r"""The real-amplitudes 2-local circuit.
+
+    The ``RealAmplitudes`` circuit is a heuristic trial wave function used as Ansatz in chemistry
+    applications or classification circuits in machine learning. The circuit consists of
+    of alternating layers of :math:`Y` rotations and :math:`CNOT` entanglements. The entanglement
+    pattern can be user-defined or selected from a predefined set.
+    It is called ``RealAmplitudes`` since the prepared quantum states will only have
+    real amplitudes, the complex part is always 0.
+
+    The entanglement can be set using the ``entanglement`` keyword as string or a list of
+    index-pairs.Additional options that can be set include the
+    number of repetitions, skipping rotation gates on qubits that are not entangled, leaving out
+    the final rotation layer and inserting barriers in between the rotation and entanglement
+    layers.
+
+    If some qubits are not entangled with other qubits it makes sense to not apply rotation gates
+    on these qubits, since a sequence of :math:`Y` rotations can be reduced to a single :math:`Y`
+    rotation with summed rotation angles.
+    """
+
+    def __init__(
+        self,
+        num_qubits: Optional[int] = None,
+        entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "reverse_linear",
+        reps: int = 3,
+        skip_unentangled_qubits: bool = False,
+        skip_final_rotation_layer: bool = False,
+        parameter_prefix: str = "θ",
+        insert_barriers: bool = False,
+        initial_state: Optional[Any] = None,
+        name: str = "RealAmplitudes",
+    ) -> None:
+        """Create a new RealAmplitudes 2-local circuit.
+
+        Args:
+            num_qubits: The number of qubits of the RealAmplitudes circuit.
+            reps: Specifies how often the structure of a rotation layer followed by an entanglement
+                layer is repeated.
+            entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear'
+                'reverse_linear, 'circular' or 'sca'), a list of integer-pairs specifying the indices
+                of qubits entangled with one another, or a callable returning such a list provided with
+                the index of the entanglement layer.
+                Default to 'reverse_linear' entanglement.
+                Note that 'reverse_linear' entanglement provides the same unitary as 'full'
+                with fewer entangling gates.
+            initial_state: A `QuantumCircuit` object to prepend to the circuit.
+            skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits
+                that are entangled with another qubit. If False, the single qubit gates are applied
+                to each qubit in the Ansatz. Defaults to False.
+            skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits
+                that are entangled with another qubit. If False, the single qubit gates are applied
+                to each qubit in the Ansatz. Defaults to False.
+            skip_final_rotation_layer: If False, a rotation layer is added at the end of the
+                ansatz. If True, no rotation layer is added.
+            parameter_prefix: The parameterized gates require a parameter to be defined
+            insert_barriers: If True, barriers are inserted in between each layer. If False,
+                no barriers are inserted.
+
+        """
+        super().__init__(
+            num_qubits=num_qubits,
+            reps=reps,
+            rotation_blocks=RY,
+            entanglement_blocks=CNOT,
+            entanglement=entanglement,
+            initial_state=initial_state,
+            skip_unentangled_qubits=skip_unentangled_qubits,
+            skip_final_rotation_layer=skip_final_rotation_layer,
+            parameter_prefix=parameter_prefix,
+            insert_barriers=insert_barriers,
+            name=name,
+        )
+
+    @property
+    def parameter_bounds(self) -> List[Tuple[float, float]]:
+        """Return the parameter bounds.
+
+        Returns:
+            The parameter bounds.
+        """
+        return self.num_parameters * [(-np.pi, np.pi)]
diff --git a/torchquantum/n_local/two_local.py b/torchquantum/n_local/two_local.py
new file mode 100644
index 00000000..d4a8ce57
--- /dev/null
+++ b/torchquantum/n_local/two_local.py
@@ -0,0 +1,249 @@
+"""The two-local gate circuit."""
+
+from __future__ import annotations
+from typing import Union, Optional, List, Callable, Any, Sequence
+
+
+from torchquantum.devices import QuantumDevice
+
+#Gate in qiskit is a unitary gate with argument "instruction". Parameter is an unknown parameter for gates
+# which can possibly be trained. We need to find appropriate replacements
+from qiskit.circuit import Gate, Parameter
+
+
+from .n_local import NLocal
+from torchquantum.operators import (
+    I,
+    PauliZ,
+    PauliY,
+    PauliZ,
+    RX,
+    RY,
+    RZ,
+    H,
+    S,
+    SDG,
+    T,
+    TDG,
+    RXX,
+    RYY,
+    RZX,
+    RZZ,
+    SWAP,
+    CNOT,
+    CY,
+    CZ,
+    CRX,
+    CRY,
+    CRZ,
+    CH,
+)
+
+class TwoLocal(NLocal):
+    r"""The two-local circuit.
+
+    The two-local circuit is a parameterized circuit consisting of alternating rotation layers and
+    entanglement layers. The rotation layers are single qubit gates applied on all qubits.
+    The entanglement layer uses two-qubit gates to entangle the qubits according to a strategy set
+    using ``entanglement``. Both the rotation and entanglement gates can be specified as
+    string (e.g. ``'ry'`` or ``'cnot'``), as gate-type (e.g. ``RY`` or ``CNOT``) or
+    as QuantumCircuit (e.g. a 1-qubit circuit or 2-qubit circuit).
+
+    A set of default entanglement strategies is provided:
+
+    * ``'full'`` entanglement is each qubit is entangled with all the others.
+    * ``'linear'`` entanglement is qubit :math:`i` entangled with qubit :math:`i + 1`,
+      for all :math:`i \in \{0, 1, ... , n - 2\}`, where :math:`n` is the total number of qubits.
+    * ``'reverse_linear'`` entanglement is qubit :math:`i` entangled with qubit :math:`i + 1`,
+      for all :math:`i \in \{n-2, n-3, ... , 1, 0\}`, where :math:`n` is the total number of qubits.
+      Note that if ``entanglement_blocks = 'cnot'`` then this option provides the same unitary as
+      ``'full'`` with fewer entangling gates.
+    * ``'pairwise'`` entanglement is one layer where qubit :math:`i` is entangled with qubit
+      :math:`i + 1`, for all even values of :math:`i`, and then a second layer where qubit :math:`i`
+      is entangled with qubit :math:`i + 1`, for all odd values of :math:`i`.
+    * ``'circular'`` entanglement is linear entanglement but with an additional entanglement of the
+      first and last qubit before the linear part.
+    * ``'sca'`` (shifted-circular-alternating) entanglement is a generalized and modified version
+      of the proposed circuit 14 in `Sim et al. <https://arxiv.org/abs/1905.10876>`__.
+      It consists of circular entanglement where the 'long' entanglement connecting the first with
+      the last qubit is shifted by one each block.  Furthermore the role of control and target
+      qubits are swapped every block (therefore alternating).
+
+    The entanglement can further be specified using an entangler map, which is a list of index
+    pairs, such as
+
+    >>> entangler_map = [(0, 1), (1, 2), (2, 0)]
+
+    If different entanglements per block should be used, provide a list of entangler maps.
+    See the examples below on how this can be used.
+
+    >>> entanglement = [entangler_map_layer_1, entangler_map_layer_2, ... ]
+
+    Barriers can be inserted in between the different layers for better visualization using the
+    ``insert_barriers`` attribute.
+
+    For each parameterized gate a new parameter is generated using a
+    :class:`~qiskit.circuit.library.ParameterVector`. The name of these parameters can be chosen
+    using the ``parameter_prefix``.
+
+    """
+
+    def __init__(
+        self,
+        num_qubits: Optional[int] = None,
+        rotation_blocks: Optional[
+            Union[str, List[str], type, List[type], QuantumDevice, List[QuantumDevice]]
+        ] = None,
+        entanglement_blocks: Optional[
+            Union[str, List[str], type, List[type], QuantumDevice, List[QuantumDevice]]
+        ] = None,
+        entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full",
+        reps: int = 3,
+        skip_unentangled_qubits: bool = False,
+        skip_final_rotation_layer: bool = False,
+        parameter_prefix: str = "θ",
+        insert_barriers: bool = False,
+        initial_state: Optional[Any] = None,
+        name: str = "TwoLocal",
+    ) -> None:
+        """Construct a new two-local circuit.
+
+        Args:
+            num_qubits: The number of qubits of the two-local circuit.
+            rotation_blocks: The gates used in the rotation layer. Can be specified via the name of
+                a gate (e.g. ``'ry'``) or the gate type itself (e.g. :class:`.RYGate`).
+                If only one gate is provided, the gate same gate is applied to each qubit.
+                If a list of gates is provided, all gates are applied to each qubit in the provided
+                order.
+                See the Examples section for more detail.
+            entanglement_blocks: The gates used in the entanglement layer. Can be specified in
+                the same format as ``rotation_blocks``.
+            entanglement: Specifies the entanglement structure. Can be a string (``'full'``,
+                ``'linear'``, ``'reverse_linear'``, ``'circular'`` or ``'sca'``),
+                a list of integer-pairs specifying the indices
+                of qubits entangled with one another, or a callable returning such a list provided with
+                the index of the entanglement layer.
+                Default to ``'full'`` entanglement.
+                Note that if ``entanglement_blocks = 'cx'``, then ``'full'`` entanglement provides the
+                same unitary as ``'reverse_linear'`` but the latter option has fewer entangling gates.
+                See the Examples section for more detail.
+            reps: Specifies how often a block consisting of a rotation layer and entanglement
+                layer is repeated.
+            skip_unentangled_qubits: If ``True``, the single qubit gates are only applied to qubits
+                that are entangled with another qubit. If ``False``, the single qubit gates are applied
+                to each qubit in the ansatz. Defaults to ``False``.
+            skip_final_rotation_layer: If ``False``, a rotation layer is added at the end of the
+                ansatz. If ``True``, no rotation layer is added.
+            parameter_prefix: The parameterized gates require a parameter to be defined, for which
+                we use instances of :class:`~qiskit.circuit.Parameter`. The name of each parameter will
+                be this specified prefix plus its index.
+            insert_barriers: If ``True``, barriers are inserted in between each layer. If ``False``,
+                no barriers are inserted. Defaults to ``False``.
+            initial_state: A :class:`.QuantumCircuit` object to prepend to the circuit.
+
+        """
+        super().__init__(
+            num_qubits=num_qubits,
+            rotation_blocks=rotation_blocks,
+            entanglement_blocks=entanglement_blocks,
+            entanglement=entanglement,
+            reps=reps,
+            skip_final_rotation_layer=skip_final_rotation_layer,
+            skip_unentangled_qubits=skip_unentangled_qubits,
+            insert_barriers=insert_barriers,
+            initial_state=initial_state,
+            parameter_prefix=parameter_prefix,
+            name=name,
+        )
+
+    def _convert_to_block(self, layer: Union[str, type, Gate, QuantumDevice]) -> QuantumDevice:
+        """For a layer provided as str (e.g. ``'ry'``) or type (e.g. :class:`.RYGate`) this function
+         returns the
+         according layer type along with the number of parameters (e.g. ``(RYGate, 1)``).
+
+        Args:
+            layer: The qubit layer.
+
+        Returns:
+            The specified layer with the required number of parameters.
+
+        Raises:
+            TypeError: The type of ``layer`` is invalid.
+            ValueError: The type of ``layer`` is str but the name is unknown.
+            ValueError: The type of ``layer`` is type but the layer type is unknown.
+
+        Note:
+            Outlook: If layers knew their number of parameters as static property, we could also
+            allow custom layer types.
+        """
+        if isinstance(layer, QuantumDevice):
+            return layer
+
+        # check the list of valid layers
+        # this could be a lot easier if the standard layers would have ``name`` and ``num_params``
+        # as static types, which might be something they should have anyway
+        theta = Parameter("θ")
+        valid_layers = {
+            "ch": CH(),
+            "cx": CNOT(),
+            "cnot": CNOT(),
+            "cy": CY(),
+            "cz": CZ(),
+            "crx": CRX(theta),
+            "cry": CRY(theta),
+            "crz": CRZ(theta),
+            "h": H(),
+            "i": I(),
+            "id": I(),
+            "iden": I(),
+            "rx": RX(theta),
+            "rxx": RXX(theta),
+            "ry": RY(theta),
+            "ryy": RYY(theta),
+            "rz": RZ(theta),
+            "rzx": RZX(theta),
+            "rzz": RZZ(theta),
+            "s": S(),
+            "sdg": SDG(),
+            "swap": SWAP(),
+            "x": X(),
+            "y": Y(),
+            "z": Z(),
+            "t": T(),
+            "tdg": TDG(),
+        }
+
+        # try to exchange `layer` from a string to a gate instance
+        if isinstance(layer, str):
+            try:
+                layer = valid_layers[layer]
+            except KeyError as ex:
+                raise ValueError(f"Unknown layer name `{layer}`.") from ex
+
+        # try to exchange `layer` from a type to a gate instance
+        if isinstance(layer, type):
+            # iterate over the layer types and look for the specified layer
+            instance = None
+            for gate in valid_layers.values():
+                if isinstance(gate, layer):
+                    instance = gate
+            if instance is None:
+                raise ValueError(f"Unknown layer type`{layer}`.")
+            layer = instance
+
+        if isinstance(layer):
+            circuit = QuantumDevice(layer.num_qubits)
+            circuit.append(layer, list(range(layer.num_qubits)))
+            return circuit
+
+        raise TypeError(
+            f"Invalid input type {type(layer)}. " + "`layer` must be a type, str or QuantumDevice."
+        )
+
+    def get_entangler_map(
+        self, rep_num: int, block_num: int, num_block_qubits: int
+    ) -> Sequence[Sequence[int]]:
+        """Overloading to handle the special case of 1 qubit where the entanglement are ignored."""
+        if self.num_qubits <= 1:
+            return []
+        return super().get_entangler_map(rep_num, block_num, num_block_qubits)
diff --git a/torchquantum/node.py b/torchquantum/node.py
index 1581865a..f9f277d7 100644
--- a/torchquantum/node.py
+++ b/torchquantum/node.py
@@ -14,11 +14,53 @@
 
 
 class QuantumNode(tq.QuantumModule):
-    """
-    a quantum node contains a q device, encoder, q layer and measure
+    """A quantum node contains a q device, encoder, q layer and measure
+    
+    Attributes:
+        arch (dict): The architecture configuration for the node.
+        q_device (tq.QuantumDevice): The quantum device.
+        encoder (tq.GeneralEncoder): The encoder.
+        q_layer (tq.QuantumLayer): The quantum layer.
+        measure (tq.MeasureAll): The measurement operator.
+        act_norm (str): The activation normalization method.
+        x_before_add_noise (torch.Tensor): The input tensor before adding noise.
+        x_before_add_noise_second (torch.Tensor): The input tensor before adding noise (second step).
+        x_before_act_quant (torch.Tensor): The input tensor before activation quantization.
+        x_before_norm (torch.Tensor): The input tensor before normalization.
+        circuit_in (torch.Tensor): The input tensor of the circuit.
+        circuit_out (torch.Tensor): The output tensor of the circuit.
+        shift_this_step (List[bool]): The list indicating whether to shift parameters in each step.
+        cool_down (List[int]): The cooling down values.
+        triger_cd (List[int]): The trigger cooling down values.
+        bn (torch.nn.BatchNorm1d): The batch normalization layer.
+        node_id (int): The ID of the node.
+        pre_specified_mean_std (dict): The pre-specified mean and standard deviation.
+        grad_qlayer (List[Optional[torch.Tensor]]): The gradients of the quantum layer.
+        grad_encoder (List[Optional[torch.Tensor]]): The gradients of the encoder.
+    
+    Methods:
+        __init__(self, arch, act_norm, node_id): Initialize the QuantumNode.
+        forward(self, x, use_qiskit=False, is_last_node=False, parallel=True): Forward pass of the QuantumNode.
+        run_circuit(self, inputs): Run the circuit without applying normalization or noise.
+        shift_and_run(self, x, use_qiskit=False, is_last_node=False, is_first_node=False, parallel=True):
+            Shift the parameters and run the circuit.
     """
 
     def __init__(self, arch, act_norm, node_id):
+        """Initialize the QuantumNode.
+
+        Args:
+            arch (dict): The architecture configuration for the node.
+            act_norm (str): The activation normalization method.
+            node_id (int): The ID of the node.
+
+        Example:
+            >>> arch = {"n_wires": 2, "encoder_op_list_name": "default", "q_layer_name": "default"}
+            >>> act_norm = "batch_norm"
+            >>> node_id = 0
+            >>> node = QuantumNode(arch, act_norm, node_id)
+        """
+        
         super().__init__()
         self.arch = arch
         self.q_device = tq.QuantumDevice(n_wires=arch["n_wires"])
@@ -50,6 +92,26 @@ def __init__(self, arch, act_norm, node_id):
         self.grad_encoder = None
 
     def forward(self, x, use_qiskit=False, is_last_node=False, parallel=True):
+        """Forward pass of the QuantumNode.
+
+        Args:
+            x (torch.Tensor): The input tensor.
+                Defaults to False.
+            use_qiskit (bool): Whether to use the Qiskit processor.
+                Defaults to False.
+            is_last_node (bool): Whether the node is the last node in the model.
+                Defaults to False.
+            parallel (bool): Whether to run the circuit in parallel.
+                Defaults to True.
+
+        Returns:
+            torch.Tensor: The output tensor.
+
+        Example:
+            >>> x = torch.randn(10, 2)
+            >>> node.forward(x, use_qiskit=True, is_last_node=False, parallel=True)
+        """
+        
         if use_qiskit:
             x = self.qiskit_processor.process_parameterized(
                 self.q_device,
@@ -121,6 +183,19 @@ def forward(self, x, use_qiskit=False, is_last_node=False, parallel=True):
         return x
 
     def run_circuit(self, inputs):
+        """Run the circuit without applying normalization or noise.
+
+        Args:
+            inputs (torch.Tensor): The input tensor.
+
+        Returns:
+            torch.Tensor: The output tensor.
+
+        Example:
+            >>> inputs = torch.randn(10, 2)
+            >>> node.run_circuit(inputs)
+        """
+        
         self.encoder(self.q_device, inputs)
         self.q_layer(self.q_device)
         x = self.measure(self.q_device)
@@ -134,6 +209,28 @@ def shift_and_run(
         is_first_node=False,
         parallel=True,
     ):
+        """Shift the parameters and run the circuit.
+
+        Args:
+            x (torch.Tensor): The input tensor.
+            use_qiskit (bool): Whether to use the Qiskit processor.
+                Defaults to False.
+            is_last_node (bool): Whether the node is the last node in the model.
+                Defaults to False.
+            is_first_node (bool): Whether the node is the first node in the model.
+                Defaults to False.
+            parallel (bool): Whether to run the circuit in parallel.
+                Defaults to True.
+
+        Returns:
+            torch.Tensor: The output tensor.
+            datetime.timedelta: The time spent during the circuit execution.
+
+        Example:
+            >>> x = torch.randn(10, 2)
+            >>> node.shift_and_run(x, use_qiskit=True, is_last_node=False, is_first_node=False, parallel=True)
+        """
+        
         import numpy as np
 
         self.circuit_in = x
@@ -293,7 +390,23 @@ def shift_and_run(
         return x, time_spent
 
 
-def build_nodes(node_archs, act_norm=None):
+def build_nodes(node_archs, act_norm=None) -> torchquantum.QuantumModuleList:
+    """Build a list of QuantumNode instances based on the given architectures.
+
+    Args:
+        node_archs (list): The list of node architectures.
+        act_norm (str): The activation normalization method.
+            Defaults to None.
+
+    Returns:
+        torchquantum.QuantumModuleList: The list of QuantumNode instances.
+        
+    Example:
+        >>> node_archs = [...]
+        >>> act_norm = "batch_norm"
+        >>> nodes = build_nodes(node_archs, act_norm)
+    """
+    
     nodes = tq.QuantumModuleList()
     for k, node_arch in enumerate(node_archs):
         nodes.append(QuantumNode(node_arch, act_norm=act_norm, node_id=k))
diff --git a/torchquantum/noise_model.py b/torchquantum/noise_model.py
index 98166abf..d7e7478e 100644
--- a/torchquantum/noise_model.py
+++ b/torchquantum/noise_model.py
@@ -25,6 +25,33 @@ def cos_adjust_noise(
     prob_schedule_separator,
     orig_noise_total_prob,
 ):
+    """
+        Adjust the noise probability based on the current epoch and a cosine schedule.
+
+        Args:
+            current_epoch (int): The current epoch.
+            n_epochs (int): The total number of epochs.
+            prob_schedule (str): The probability schedule type. Possible values are:
+                - None: No schedule, use the original noise probability.
+                - "increase": Increase the noise probability using a cosine schedule.
+                - "decrease": Decrease the noise probability using a cosine schedule.
+                - "increase_decrease": Increase the noise probability until a separator epoch,
+                  then decrease it using cosine schedules.
+            prob_schedule_separator (int): The epoch at which the schedule changes for
+                "increase_decrease" mode.
+            orig_noise_total_prob (float): The original noise probability.
+
+        Returns:
+            float: The adjusted noise probability based on the schedule.
+
+        Note:
+            The adjusted noise probability is returned as a float between 0 and 1.
+
+        Raises:
+            None.
+
+        """
+
     if prob_schedule is None:
         noise_total_prob = orig_noise_total_prob
     elif prob_schedule == "increase":
@@ -82,6 +109,32 @@ def cos_adjust_noise(
 
 
 def apply_readout_error_func(x, c2p_mapping, measure_info):
+    """
+        Apply readout error to the measurement outcomes.
+
+        Args:
+            x (torch.Tensor): The measurement outcomes, represented as a tensor of shape (batch_size, num_qubits).
+            c2p_mapping (dict): Mapping from qubit indices to physical wire indices.
+            measure_info (dict): Measurement information dictionary containing the probabilities for different outcomes.
+
+        Returns:
+            torch.Tensor: The measurement outcomes after applying the readout error, represented as a tensor of the same shape as x.
+
+        Note:
+            The readout error is applied based on the given mapping and measurement information.
+            The measurement information dictionary should have the following structure:
+            {
+                (wire_1,): {"probabilities": [[p_0, p_1], [p_0, p_1]]},
+                (wire_2,): {"probabilities": [[p_0, p_1], [p_0, p_1]]},
+                ...
+            }
+            where wire_1, wire_2, ... are the physical wire indices, and p_0 and p_1 are the probabilities of measuring 0 and 1, respectively,
+            for each wire.
+
+        Raises:
+            None.
+
+        """
     # add readout error
     noise_free_0_probs = (x + 1) / 2
     noise_free_1_probs = 1 - (x + 1) / 2
@@ -118,8 +171,22 @@ def apply_readout_error_func(x, c2p_mapping, measure_info):
 
 
 class NoiseCounter:
-    '''count all the pauli error gate being sampled in self.sample_noise_op()
-    '''
+    """
+        A class for counting the occurrences of Pauli error gates.
+
+        Attributes:
+            counter_x (int): Counter for Pauli X errors.
+            counter_y (int): Counter for Pauli Y errors.
+            counter_z (int): Counter for Pauli Z errors.
+            counter_X (int): Counter for Pauli X errors (for two-qubit gates).
+            counter_Y (int): Counter for Pauli Y errors (for two-qubit gates).
+            counter_Z (int): Counter for Pauli Z errors (for two-qubit gates).
+
+        Methods:
+            add(error): Adds a Pauli error to the counters based on the error type.
+            __str__(): Returns a string representation of the counters.
+
+        """
     def __init__(self):
         self.counter_x = 0
         self.counter_y = 0
@@ -152,8 +219,28 @@ def __str__(self) -> str:
 
 class NoiseModelTQ(object):
     """
-    apply gate insertion and readout
-    """
+        A class for applying gate insertion and readout errors.
+
+        Attributes:
+            noise_model_name (str): Name of the noise model.
+            n_epochs (int): Number of epochs.
+            noise_total_prob (float): Total probability of noise.
+            ignored_ops (tuple): Operations to be ignored.
+            prob_schedule (list): Probability schedule.
+            prob_schedule_separator (str): Separator for probability schedule.
+            factor (float): Factor for adjusting probabilities.
+            add_thermal (bool): Flag indicating whether to add thermal relaxation.
+
+        Methods:
+            adjust_noise(current_epoch): Adjusts the noise based on the current epoch.
+            clean_parsed_noise_model_dict(nm_dict, ignored_ops): Cleans the parsed noise model dictionary.
+            parse_noise_model_dict(nm_dict): Parses the noise model dictionary.
+            magnify_probs(probs): Magnifies the probabilities based on a factor.
+            sample_noise_op(op_in): Samples a noise operation based on the given operation.
+            apply_readout_error(x): Applies readout error to the input.
+
+        """
+
 
     def __init__(
         self,
@@ -363,8 +450,25 @@ def apply_readout_error(self, x):
 
 class NoiseModelTQActivation(object):
     """
-    add noise to the activations
-    """
+        A class for adding noise to the activations.
+
+        Attributes:
+            mean (tuple): Mean values of the noise.
+            std (tuple): Standard deviation values of the noise.
+            n_epochs (int): Number of epochs.
+            prob_schedule (list): Probability schedule.
+            prob_schedule_separator (str): Separator for probability schedule.
+            after_norm (bool): Flag indicating whether noise should be added after normalization.
+            factor (float): Factor for adjusting the noise.
+
+        Methods:
+            adjust_noise(current_epoch): Adjusts the noise based on the current epoch.
+            sample_noise_op(op_in): Samples a noise operation.
+            apply_readout_error(x): Applies readout error to the input.
+            add_noise(x, node_id, is_after_norm): Adds noise to the activations.
+
+        """
+
 
     def __init__(
         self,
@@ -432,8 +536,23 @@ def add_noise(self, x, node_id, is_after_norm=False):
 
 class NoiseModelTQPhase(object):
     """
-    add noise to rotation parameters
-    """
+        A class for adding noise to rotation parameters.
+
+        Attributes:
+            mean (float): Mean value of the noise.
+            std (float): Standard deviation value of the noise.
+            n_epochs (int): Number of epochs.
+            prob_schedule (list): Probability schedule.
+            prob_schedule_separator (str): Separator for probability schedule.
+            factor (float): Factor for adjusting the noise.
+
+        Methods:
+            adjust_noise(current_epoch): Adjusts the noise based on the current epoch.
+            sample_noise_op(op_in): Samples a noise operation.
+            apply_readout_error(x): Applies readout error to the input.
+            add_noise(phase): Adds noise to the rotation parameters.
+
+        """
 
     def __init__(
         self,
@@ -494,16 +613,41 @@ def add_noise(self, phase):
 
 
 class NoiseModelTQReadoutOnly(NoiseModelTQ):
+    """
+       A subclass of NoiseModelTQ that applies readout errors only.
+
+       This class inherits from NoiseModelTQ and overrides the sample_noise_op method to exclude the insertion of any noise operations other than readout errors. It is designed for scenarios where only readout errors are considered, and all other noise sources are ignored.
+
+       Methods:
+           sample_noise_op(op_in): Returns an empty list, indicating no noise operations are applied.
+       """
     def sample_noise_op(self, op_in):
         return []
 
 
 class NoiseModelTQQErrorOnly(NoiseModelTQ):
+    """
+        A subclass of NoiseModelTQ that applies only readout errors.
+
+        This class inherits from NoiseModelTQ and overrides the apply_readout_error method to apply readout errors. It removes activation noise and only focuses on readout errors in the noise model.
+
+        Methods:
+            apply_readout_error(x): Applies readout error to the given activation values.
+
+        """
     def apply_readout_error(self, x):
         return x
 
 
 class NoiseModelTQActivationReadout(NoiseModelTQActivation):
+    """
+       A subclass of NoiseModelTQActivation that applies readout errors.
+
+       This class inherits from NoiseModelTQActivation and overrides the apply_readout_error method to incorporate readout errors. It combines activation noise and readout errors into the noise model.
+
+       Methods:
+           apply_readout_error(x): Applies readout error to the given activation values
+       """
     def __init__(
         self,
         noise_model_name,
@@ -544,6 +688,14 @@ def apply_readout_error(self, x):
 
 
 class NoiseModelTQPhaseReadout(NoiseModelTQPhase):
+    """
+        A subclass of NoiseModelTQPhase that applies readout errors to phase values.
+
+        This class inherits from NoiseModelTQPhase and overrides the apply_readout_error method to apply readout errors specifically to phase values. It uses the noise model provided to introduce readout errors.
+
+        Methods:
+            apply_readout_error(x): Applies readout error to the given phase values.
+        """
     def __init__(
         self,
         noise_model_name,
diff --git a/torchquantum/operators.py b/torchquantum/operators.py
index 42d5de04..6b5c4e51 100644
--- a/torchquantum/operators.py
+++ b/torchquantum/operators.py
@@ -66,6 +66,23 @@
     "SingleExcitation",
     "EchoedCrossResonance",
     "ECR",
+    "QFT",
+    "SDG",
+    "TDG",
+    'SXDG',
+    "CH",
+    "CCZ",
+    "ISWAP",
+    "CS",
+    "CSDG",
+    "CSX",
+    "CHadamard",
+    "CCZ",
+    "DCX",
+    "XXMINYY",
+    "XXPLUSYY",
+    "C3X",
+    "R",
 ]
 
 
@@ -122,6 +139,19 @@ class Operator(tq.QuantumModule):
         "MultiXCNOT",
         "Reset",
         "EchoedCrossResonance",
+        "QFT",
+        "SDG",
+        "TDG",
+        "SXDG",
+        "CH",
+        "CCZ",
+        "ISWAP",
+        "CS",
+        "CSDG",
+        "CSX",
+        "CHadamard",
+        "DCX",
+        "C3X",
     ]
 
     parameterized_ops = [
@@ -150,6 +180,9 @@ class Operator(tq.QuantumModule):
         "TrainableUnitary",
         "TrainableUnitaryStrict",
         "SingleExcitation",
+        "XXMINYY",
+        "XXPLUSYY",
+        "R",
     ]
 
     @property
@@ -576,6 +609,8 @@ def _matrix(cls, params):
         return cls.matrix
 
 
+   
+
 class PauliX(Observable, metaclass=ABCMeta):
     """Class for Pauli X Gate."""
 
@@ -713,6 +748,8 @@ def _matrix(cls, params):
     def _eigvals(cls, params):
         return cls.eigvals
 
+   
+
 
 class CNOT(Operation, metaclass=ABCMeta):
     """Class for CNOT Gate."""
@@ -937,6 +974,7 @@ class TrainableUnitary(Operation, metaclass=ABCMeta):
     num_wires = AnyWires
     func = staticmethod(tqf.qubitunitaryfast)
 
+
     def build_params(self, trainable):
         """Build the parameters for the gate.
 
@@ -1316,12 +1354,217 @@ class ECR(Operation, metaclass=ABCMeta):
     @classmethod
     def _matrix(cls, params):
         return cls.matrix
+    
+class QFT(Observable, metaclass=ABCMeta):
+    """Class for Quantum Fourier Transform."""
+
+    num_params = 0
+    num_wires = AnyWires
+    func = staticmethod(tqf.qft)
+
+    @classmethod
+    def _matrix(cls, params, n_wires):
+        return tqf.qft_matrix(n_wires)
+
+
+class SDG(Operation, metaclass=ABCMeta):
+    """Class for SDG Gate."""
+
+    num_params = 0
+    num_wires = 1
+
+    matrix = mat_dict["sdg"]
+    func = staticmethod(tqf.sdg)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+
+class TDG(Operation, metaclass=ABCMeta):
+    """Class for TDG Gate."""
+
+    num_params = 0
+    num_wires = 1
+    matrix = mat_dict["tdg"]
+    func = staticmethod(tqf.tdg)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+class SXDG(Operation, metaclass=ABCMeta):
+    """Class for SXDG Gate."""
+
+    num_params = 0
+    num_wires = 1
+    matrix = mat_dict["sxdg"]
+    func = staticmethod(tqf.sxdg)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
 
 
+class CCZ(Operation, metaclass=ABCMeta):
+    """Class for CCZ Gate."""
+
+    num_params = 0
+    num_wires = 3
+    matrix = mat_dict["ccz"]
+    func = staticmethod(tqf.ccz)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+class ISWAP(Operation, metaclass=ABCMeta):
+    """Class for ISWAP Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["iswap"]
+    func = staticmethod(tqf.iswap)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+
+class CS(Operation, metaclass=ABCMeta):
+    """Class for CS Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["cs"]
+    eigvals = np.array([1, 1, 1, 1j])
+    func = staticmethod(tqf.cs)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+   
+    @classmethod
+    def _eigvals(cls, params):
+        return cls.eigvals
+    
+class CSDG(DiagonalOperation, metaclass=ABCMeta):
+    """Class for CS Dagger Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["csdg"]
+    eigvals = np.array([1, 1, 1, -1j])
+    func = staticmethod(tqf.csdg)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+    @classmethod
+    def _eigvals(cls, params):
+        return cls.eigvals
+ 
+class CSX(Operation, metaclass=ABCMeta):
+    """Class for CSX Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["csx"]
+    func = staticmethod(tqf.csx)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+class CHadamard(Operation, metaclass=ABCMeta):
+    """Class for CHadamard Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["chadamard"]
+    func = staticmethod(tqf.chadamard)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+
+class CCZ(DiagonalOperation, metaclass=ABCMeta):
+    """Class for CCZ Gate."""
+
+    num_params = 0
+    num_wires = 3
+    matrix = mat_dict["ccz"]
+    eigvals = np.array([1, 1, 1, 1, 1, 1, 1, -1])
+    func = staticmethod(tqf.ccz)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+    @classmethod
+    def _eigvals(cls, params):
+        return cls.eigvals
+    
+class DCX(Operation, metaclass=ABCMeta):
+    """Class for DCX Gate."""
+
+    num_params = 0
+    num_wires = 2
+    matrix = mat_dict["dcx"]
+    func = staticmethod(tqf.dcx)
+
+    @classmethod
+    def _matrix(cls, params):
+        return cls.matrix
+    
+class XXMINYY(Operation, metaclass=ABCMeta):
+    """Class for XXMinusYY gate."""
+
+    num_params = 2
+    num_wires = 2
+    func = staticmethod(tqf.xxminyy_matrix)
+
+    @classmethod
+    def _matrix(cls, params):
+        return tqf.xxminyy_matrix(params)
+    
+class XXPLUSYY(Operation, metaclass=ABCMeta):
+    """Class for XXPlusYY gate."""
+
+    num_params = 2
+    num_wires = 2
+    func = staticmethod(tqf.xxplusyy_matrix)
+
+    @classmethod
+    def _matrix(cls, params):
+        return tqf.xxplusyy_matrix(params)
+    
+class C3X(Operation, metaclass=ABCMeta):
+    """Class for C3X gate."""
+    
+    num_params = 0
+    num_wires = 4
+    func = staticmethod(tqf.c3x)
+    
+    @classmethod
+    def _matrix(cls, params):
+        return tqf.qubitunitary_matrix(mat_dict['toffoli'])
+
+class R(DiagonalOperation, metaclass=ABCMeta):
+    """Class for R Gate."""
+
+    num_params = 1
+    num_wires = 1
+    func = staticmethod(tqf.r)
+
+    @classmethod
+    def _matrix(cls, params):
+        return tqf.r_matrix(params)
+
 H = Hadamard
 SH = SHadamard
 EchoedCrossResonance = ECR
-
+CH = CHadamard
 
 op_name_dict = {
     "hadamard": Hadamard,
@@ -1388,4 +1631,21 @@ def _matrix(cls, params):
     "singleexcitation": SingleExcitation,
     "ecr": ECR,
     "echoedcrossresonance": ECR,
+    "QFT": QFT,
+    "sdg": SDG,
+    "cs": CS,
+    "chadamard": CHadamard,
+    "ch": CH,
+    "dcx":DCX,
+    "xxminyy": XXMINYY,
+    "xxplusyy": XXPLUSYY,
+    "c3x": C3X,
+    "tdg": TDG,
+    "sxdg": SXDG,
+    "ch": CH,
+    "ccz": CCZ,
+    "iswap": ISWAP,
+    "csdg": CSDG,
+    "csx": CSX,
+    "r": R,
 }
diff --git a/torchquantum/plugins/qiskit_pulse.py b/torchquantum/plugins/qiskit_pulse.py
index 17e32cf9..81775b0d 100644
--- a/torchquantum/plugins/qiskit_pulse.py
+++ b/torchquantum/plugins/qiskit_pulse.py
@@ -8,6 +8,23 @@
 
 
 def circ2pulse(circuits, name):
+    """
+    Convert a circuit to a pulse schedule using the specified backend.
+
+    Args:
+        circuits (QuantumCircuit): The input quantum circuit.
+        name (str): The name of the backend.
+
+    Returns:
+        None.
+
+    Example:
+        >>> qc = QuantumCircuit(2)
+        >>> qc.h(0)
+        >>> qc.cx(0, 1)
+        >>> circ2pulse(qc, 'ibmq_oslo')
+    """
+    
     if name in IBMQ_PNAMES:
         backend = name()
         with pulse.build(backend) as pulse_tq:
diff --git a/torchquantum/prune_utils.py b/torchquantum/prune_utils.py
index 951e9543..64a21019 100644
--- a/torchquantum/prune_utils.py
+++ b/torchquantum/prune_utils.py
@@ -17,13 +17,22 @@
 
 
 class PhaseL1UnstructuredPruningMethod(torch.nn.utils.prune.BasePruningMethod):
-    """
-    Prune rotation phases which is close to -2pi, 0, +2pi, ..
-    """
+    """Prune rotation phases which are close to -2pi, 0, +2pi, etc."""
 
     PRUNING_TYPE = "unstructured"
 
     def __init__(self, amount):
+        """Initializes a PhaseL1UnstructuredPruningMethod object.
+
+        Args:
+            amount (float or int): The pruning amount. If it is a float, it represents the fraction
+                of rotation phases to prune. If it is an integer, it represents the exact number
+                of rotation phases to prune.
+
+        Example:
+            >>> pruning_method = PhaseL1UnstructuredPruningMethod(0.2)
+        """
+        
         super().__init__()
         # Check range of validity of pruning amount
         # noinspection PyProtectedMember
@@ -31,6 +40,19 @@ def __init__(self, amount):
         self.amount = amount
 
     def compute_mask(self, t, default_mask):
+        """Computes the mask for pruning the rotation phases.
+
+        Args:
+            t (torch.Tensor): The tensor containing the rotation phases.
+            default_mask (torch.Tensor): The default mask.
+
+        Returns:
+            torch.Tensor: The computed mask for pruning.
+
+        Example:
+            >>> mask = pruning_method.compute_mask(tensor, mask)
+        """
+
         t = t % (2 * np.pi)
         t[t > np.pi] -= 2 * np.pi
 
@@ -59,11 +81,21 @@ def compute_mask(self, t, default_mask):
 
 
 class ThresholdScheduler(object):
-    """
-    Smooth increasing threshold with tensorflow model pruning scheduler
-    """
+    """Smooth increasing threshold with tensorflow model pruning scheduler"""
 
     def __init__(self, step_beg, step_end, thres_beg, thres_end):
+        """Initializes a ThresholdScheduler object.
+
+        Args:
+            step_beg (int): The step at which the threshold begins to change.
+            step_end (int): The step at which the threshold stops changing.
+            thres_beg (float): The initial threshold value.
+            thres_end (float): The final threshold value.
+
+        Example:
+            >>> scheduler = ThresholdScheduler(0, 100, 0.1, 0.9)
+        """
+
         config = tf.compat.v1.ConfigProto()
         config.gpu_options.allow_growth = True
         tf.compat.v1.enable_eager_execution(config=config)
@@ -90,6 +122,15 @@ def __init__(self, step_beg, step_end, thres_beg, thres_end):
         self.global_step = 0
 
     def step(self):
+        """Computes the threshold for the current step.
+
+        Returns:
+            float: The computed threshold.
+
+        Example:
+            >>> threshold = scheduler.step()
+        """
+
         if self.global_step < self.step_beg:
             return self.thres_beg
         elif self.global_step > self.step_end:
diff --git a/torchquantum/pulse.py b/torchquantum/pulse.py
index 33543f2d..a71e132b 100644
--- a/torchquantum/pulse.py
+++ b/torchquantum/pulse.py
@@ -13,11 +13,21 @@
 
 
 class QuantumPulse(nn.Module):
-    """
-    The Quantum Pulse simulator
+    """The Quantum Pulse simulator
+    
+    Methods:
+        __init__(self):
+            Initialize the QuantumPulse.
+        forward(self)
     """
 
     def __init__(self):
+        """Initialize the Quantum Pulse simulator.
+        
+        Returns:
+            None.
+        """
+        
         super().__init__()
         pass
 
@@ -33,6 +43,24 @@ def __init__(
         delta_t: float = 1.0,
         initial_shape: List[float] = None,
     ):
+        """Initializes a QuantumPulseDirect object.
+
+        Args:
+            n_steps (int): The number of time steps.
+            hamil: The Hamiltonian.
+            delta_t (float, optional): The time step size.
+                Defaults to 1.0.
+            initial_shape (List[float], optional): The initial shape of the pulse. 
+                Defaults to None.
+
+        Raises:
+            AssertionError: If the length of initial_shape is not equal to n_steps.
+
+        Example:
+            >>> hamiltonian = ...
+            >>> pulse = QuantumPulseDirect(n_steps=10, hamil=hamiltonian, delta_t=0.1)
+        """
+        
         super().__init__()
         self.hamil = torch.tensor(hamil, dtype=torch.complex64)
         if initial_shape is not None:
@@ -45,6 +73,16 @@ def __init__(
         self.delta_t = delta_t
 
     def get_unitary(self):
+        """Computes the unitary evolution operator for the pulse.
+
+        Returns:
+            torch.Tensor: The unitary evolution operator.
+
+        Example:
+            >>> pulse = QuantumPulseDirect(n_steps=10, hamil=hamiltonian, delta_t=0.1)
+            >>> unitary = pulse.get_unitary()
+        """
+        
         unitary_per_step = []
         for k in range(self.n_steps):
             magnitude = self.pulse_shape[k]
@@ -65,6 +103,18 @@ def get_unitary(self):
         return u_overall
 
     def __repr__(self):
+        """Returns the string representation of the QuantumPulseDirect object.
+
+        Returns:
+            str: The string representation.
+
+        Example:
+            >>> pulse = QuantumPulseDirect(n_steps=10, hamil=hamiltonian, delta_t=0.1)
+            >>> print(pulse)
+            QuantumPulse Direct
+            shape: tensor([...])
+        """
+        
         return f"QuantumPulse Direct \n shape: {self.pulse_shape}"
 
 
@@ -80,6 +130,32 @@ def __init__(
         x_max: float = 10,
         initial_params: List[float] = None,
     ):
+        """Initializes a QuantumPulseGaussian object.
+
+        Args:
+            hamil: The Hamiltonian.
+            n_steps (int, optional): The number of time steps.
+                Defaults to 100.
+            delta_t (float, optional): The time step size.
+                Defaults to 1.0.
+            x_min (float, optional): The minimum value of x.
+                Defaults to -10.
+            x_max (float, optional): The maximum value of x.
+                Defaults to 10.
+            initial_params (List[float], optional): The initial parameters of the pulse. 
+                Defaults to None.
+
+        Returns:
+            None.
+
+        Raises:
+            AssertionError: If the length of initial_params is not equal to 3.
+
+        Example:
+            >>> hamiltonian = ...
+            >>> pulse = QuantumPulseGaussian(hamil=hamiltonian, n_steps=100, delta_t=0.1)
+        """
+        
         super(QuantumPulseGaussian, self).__init__()
         self.hamil = torch.tensor(hamil, dtype=torch.complex64)
         self.delta_t = delta_t
@@ -96,6 +172,16 @@ def __init__(
         self.x_list = torch.tensor(np.arange(x_min, x_max, self.delta_x))
 
     def get_unitary(self):
+        """Computes the unitary evolution operator for the pulse.
+
+        Returns:
+            torch.Tensor: The unitary evolution operator.
+
+        Example:
+            >>> pulse = QuantumPulseGaussian(hamil=hamiltonian, n_steps=100, delta_t=0.1)
+            >>> unitary = pulse.get_unitary()
+        """
+        
         self.mag = self.pulse_params[0]
         self.mu = self.pulse_params[1]
         self.sigma = self.pulse_params[2]
@@ -131,6 +217,17 @@ def get_unitary(self):
         return u_overall
 
     def __repr__(self):
+        """Returns the string representation of the QuantumPulseGaussian object.
+
+        Returns:
+            str: The string representation.
+
+        Example:
+            >>> pulse = QuantumPulseGaussian(hamil=hamiltonian, n_steps=100, delta_t=0.1)
+            >>> print(pulse)
+            QuantumPulse Guassian
+            shape: tensor([...])
+        """
         return f"QuantumPulse Guassian \n shape: {self.pulse_shape}"
 
 
diff --git a/torchquantum/pulse/__init__.py b/torchquantum/pulse/__init__.py
new file mode 100644
index 00000000..72df532f
--- /dev/null
+++ b/torchquantum/pulse/__init__.py
@@ -0,0 +1,4 @@
+from .utils import *
+from .sesolve import sesolve
+# from .mesolve import mesolve
+# from .smesolve import smesolve
diff --git a/torchquantum/pulse/sesolve/__init__.py b/torchquantum/pulse/sesolve/__init__.py
new file mode 100644
index 00000000..c4bb790c
--- /dev/null
+++ b/torchquantum/pulse/sesolve/__init__.py
@@ -0,0 +1,1 @@
+from .sesolve import sesolve
diff --git a/torchquantum/pulse/sesolve/sesolve.py b/torchquantum/pulse/sesolve/sesolve.py
new file mode 100644
index 00000000..2faa2f58
--- /dev/null
+++ b/torchquantum/pulse/sesolve/sesolve.py
@@ -0,0 +1,43 @@
+import torch
+from ..solver import Solver
+from torchdiffeq import odeint
+
+def sesolve(
+    psi0,
+    H,
+    n_dt,
+    dt,
+    *,
+    exp_ops=None,
+    options=None,
+    dtype=None,
+    device=None
+):
+    t_save = torch.tensor(list(range(n_dt)))*dt
+
+    args = (H, psi0, t_save, exp_ops, options)
+
+    solver = SESolver(*args)
+
+    solver.run()
+
+    psi_save, exp_save = solver.y_save, solver.exp_save
+    
+    return psi_save, exp_save
+
+
+
+class SESolver(Solver):
+    
+    def __init__(self, *args):
+        super().__init__(*args)
+
+    
+    def f(self, t, y):
+        h = self.H(t)
+        return -1.j * torch.matmul(h, y)
+
+
+    def run(self):
+        self.y_save = odeint(self.f, self.psi0, self.t_save)
+        self.exp_save = None
diff --git a/torchquantum/pulse/single_qubit_demo.py b/torchquantum/pulse/single_qubit_demo.py
new file mode 100644
index 00000000..06184da9
--- /dev/null
+++ b/torchquantum/pulse/single_qubit_demo.py
@@ -0,0 +1,27 @@
+import numpy as np
+import torch
+import torchquantum as tq
+from torchquantum.pulse import sigmax, sigmay, sigmaz, sesolve
+
+
+n_dt = 10
+dt = 0.22
+
+
+initial_value = np.array([1,0])
+initial_state = torch.tensor(initial_value,dtype=torch.complex64)
+
+
+pulse_value = torch.tensor(np.ones((n_dt,1)),dtype=torch.complex64)
+pulse = torch.nn.parameter.Parameter(pulse_value)
+
+
+def H(t):
+    t_ind = (t/dt).long()
+    return -sigmaz() + sigmax() * pulse[t_ind]
+
+
+result = sesolve(psi0 = initial_state, H = H, n_dt = n_dt, dt = dt)
+
+
+print(result)
diff --git a/torchquantum/pulse/solver.py b/torchquantum/pulse/solver.py
new file mode 100644
index 00000000..6c9b0b6b
--- /dev/null
+++ b/torchquantum/pulse/solver.py
@@ -0,0 +1,18 @@
+import torch
+
+class Solver(torch.nn.Module):
+    def __init__(
+        self,
+        H,
+        psi0,
+        t_save,
+        exp_ops,
+        options
+    ):
+        self.H = H
+        self.psi0 = psi0
+        self.t_save = t_save
+        self.exp_ops = exp_ops
+        self.options = options
+
+
diff --git a/torchquantum/pulse/templates/draft.py b/torchquantum/pulse/templates/draft.py
new file mode 100644
index 00000000..e95d3aad
--- /dev/null
+++ b/torchquantum/pulse/templates/draft.py
@@ -0,0 +1,32 @@
+import os
+import argparse
+import time
+import numpy as np
+
+import torch
+import torch.nn as nn
+import torch.optim as optim
+
+
+
+class ODEFunc(nn.Module):
+
+    def __init__(self,c_ops):
+        super(ODEFunc,self).__init__()
+
+        self.c_ops = c_ops
+
+    def forward(self,t,y):
+        return 
+
+device = torch.device('cpu')
+
+y0 = torch.tensor([1,0]).to(device)
+t = torch.linspace(0., 1., 11).to(device)
+
+
+
+c_ops = torch.linspace(0.,1.,11).to(device)
+
+func = ODEFunc(c_ops).to(device)
+res_y = odeint(func,y0,t).to(device)
diff --git a/torchquantum/pulse/templates/pulse.py b/torchquantum/pulse/templates/pulse.py
new file mode 100644
index 00000000..33543f2d
--- /dev/null
+++ b/torchquantum/pulse/templates/pulse.py
@@ -0,0 +1,145 @@
+import torch
+import torch.nn as nn
+import numpy as np
+
+from typing import Union, List, Iterable
+
+
+__all__ = [
+    "QuantumPulse",
+    "QuantumPulseGaussian",
+    "QuantumPulseDirect",
+]
+
+
+class QuantumPulse(nn.Module):
+    """
+    The Quantum Pulse simulator
+    """
+
+    def __init__(self):
+        super().__init__()
+        pass
+
+    def forward(self):
+        pass
+
+
+class QuantumPulseDirect(QuantumPulse):
+    def __init__(
+        self,
+        n_steps: int,
+        hamil,
+        delta_t: float = 1.0,
+        initial_shape: List[float] = None,
+    ):
+        super().__init__()
+        self.hamil = torch.tensor(hamil, dtype=torch.complex64)
+        if initial_shape is not None:
+            assert len(initial_shape) == n_steps
+            initial_shape = torch.Tensor(initial_shape)
+        else:
+            initial_shape = torch.ones(n_steps)
+        self.pulse_shape = nn.Parameter(initial_shape)
+        self.n_steps = n_steps
+        self.delta_t = delta_t
+
+    def get_unitary(self):
+        unitary_per_step = []
+        for k in range(self.n_steps):
+            magnitude = self.pulse_shape[k]
+            unitary = torch.matrix_exp(-1j * self.hamil * magnitude * self.delta_t)
+            # print(unitary @ unitary.conj().T)
+            # unitary_mag = (unitary[0]**2).sum().sqrt()
+            # unitary = unitary_mag / unitary
+
+            unitary_per_step.append(unitary)
+
+        u_overall = None
+        for k, u in enumerate(unitary_per_step):
+            if not k:
+                u_overall = u
+            else:
+                u_overall = u_overall @ u
+
+        return u_overall
+
+    def __repr__(self):
+        return f"QuantumPulse Direct \n shape: {self.pulse_shape}"
+
+
+class QuantumPulseGaussian(QuantumPulse):
+    """Gaussian Quantum Pulse, will only count +- five sigmas"""
+
+    def __init__(
+        self,
+        hamil,
+        n_steps: int = 100,
+        delta_t: float = 1.0,
+        x_min: float = -10,
+        x_max: float = 10,
+        initial_params: List[float] = None,
+    ):
+        super(QuantumPulseGaussian, self).__init__()
+        self.hamil = torch.tensor(hamil, dtype=torch.complex64)
+        self.delta_t = delta_t
+        # mag, mu, sigma
+        if initial_params is not None:
+            assert len(initial_params) == 3
+            initial_params = torch.Tensor(initial_params)
+        else:
+            initial_params = torch.ones(3)
+
+        self.pulse_params = nn.Parameter(initial_params)
+        self.n_steps = n_steps
+        self.delta_x = (x_max - x_min) / n_steps
+        self.x_list = torch.tensor(np.arange(x_min, x_max, self.delta_x))
+
+    def get_unitary(self):
+        self.mag = self.pulse_params[0]
+        self.mu = self.pulse_params[1]
+        self.sigma = self.pulse_params[2]
+
+        # delta_x = (10 * self.sigma / self.n_steps).item()
+        # self.x_list = torch.tensor(np.arange(
+        # (self.mu - 5 * self.sigma).item(),
+        # (self.mu + 5 * self.sigma).item(), delta_x))
+
+        self.pulse_shape = self.mag * torch.exp(
+            -((self.x_list - self.mu) ** 2) / (2 * self.sigma**2)
+        )
+
+        unitary_per_step = []
+        for k in range(self.n_steps):
+            magnitude = self.pulse_shape[k]
+            unitary = torch.matrix_exp(
+                -1j * self.hamil * magnitude * self.delta_t * self.delta_x
+            )
+            # print(unitary @ unitary.conj().T)
+            # unitary_mag = (unitary[0]**2).sum().sqrt()
+            # unitary = unitary_mag / unitary
+
+            unitary_per_step.append(unitary)
+
+        u_overall = None
+        for k, u in enumerate(unitary_per_step):
+            if not k:
+                u_overall = u
+            else:
+                u_overall = u_overall @ u
+
+        return u_overall
+
+    def __repr__(self):
+        return f"QuantumPulse Guassian \n shape: {self.pulse_shape}"
+
+
+if __name__ == "__main__":
+    import pdb
+
+    pdb.set_trace()
+    pulse = QuantumPulseDirect(n_steps=10, hamil=[[0, 1], [1, 0]])
+
+    print(pulse.get_unitary())
+
+    print("finish")
diff --git a/torchquantum/pulse/templates/pulse_utils.py b/torchquantum/pulse/templates/pulse_utils.py
new file mode 100644
index 00000000..bad2a9b5
--- /dev/null
+++ b/torchquantum/pulse/templates/pulse_utils.py
@@ -0,0 +1,228 @@
+import copy
+import sched
+import qiskit
+import itertools
+import numpy as np
+
+from itertools import repeat
+from qiskit.providers import aer
+from qiskit.providers.fake_provider import *
+from qiskit.circuit import Gate
+from qiskit.compiler import assemble
+from qiskit import pulse, QuantumCircuit, IBMQ
+from qiskit.pulse.instructions import Instruction
+from qiskit.pulse.transforms import block_to_schedule
+from qiskit_nature.drivers import UnitsType, Molecule
+from scipy.optimize import minimize, LinearConstraint
+from qiskit_nature.converters.second_quantization import QubitConverter
+from qiskit_nature.properties.second_quantization.electronic import ParticleNumber
+from qiskit_nature.problems.second_quantization import ElectronicStructureProblem
+from typing import List, Tuple, Iterable, Union, Dict, Callable, Set, Optional, Any
+from qiskit.pulse import (
+    Schedule,
+    GaussianSquare,
+    Drag,
+    Delay,
+    Play,
+    ControlChannel,
+    DriveChannel,
+)
+from qiskit_nature.mappers.second_quantization import ParityMapper, JordanWignerMapper
+from qiskit_nature.transformers.second_quantization.electronic import (
+    ActiveSpaceTransformer,
+)
+from qiskit_nature.drivers.second_quantization import (
+    ElectronicStructureDriverType,
+    ElectronicStructureMoleculeDriver,
+)
+
+
+def is_parametric_pulse(t0, *inst: Union["Schedule", Instruction]):
+    inst = t0[1]
+    t0 = t0[0]
+    if isinstance(inst, pulse.Play):
+        return True
+    else:
+        return False
+
+
+def extract_ampreal(pulse_prog):
+    # extract the real part of pulse amplitude, igonred the imaginary part.
+    amp_list = list(
+        map(
+            lambda x: x[1].pulse.amp,
+            pulse_prog.filter(is_parametric_pulse).instructions,
+        )
+    )
+    amp_list = np.array(amp_list)
+    ampa_list = np.angle(np.array(amp_list))
+    return ampa_list
+
+
+def extract_amp(pulse_prog):
+    # extract the pulse amplitdue.
+    amp_list = list(
+        map(
+            lambda x: x[1].pulse.amp,
+            pulse_prog.filter(is_parametric_pulse).instructions,
+        )
+    )
+    amp_list = np.array(amp_list)
+    ampa_list = np.angle(np.array(amp_list))
+    ampn_list = np.abs(np.array(amp_list))
+    amps_list = []
+    for i, j in zip(ampn_list, ampa_list):
+        amps_list.append(i)
+        amps_list.append(j)
+    amps_list = np.array(amps_list)
+    return amps_list
+
+
+def is_phase_pulse(t0, *inst: Union["Schedule", Instruction]):
+    inst = t0[1]
+    t0 = t0[0]
+    if isinstance(inst, pulse.ShiftPhase):
+        return True
+    return False
+
+
+def extract_phase(pulse_prog):
+
+    for _, ShiftPhase in pulse_prog.filter(is_phase_pulse).instructions:
+        # print(play.pulse.amp)
+        pass
+    instructions = pulse_prog.filter(is_phase_pulse).instructions
+
+    phase_list = list(
+        map(
+            lambda x: x[1]._operands[0],
+            pulse_prog.operands[0].filter(is_phase_pulse).instructions,
+        )
+    )
+    return phase_list
+
+
+def cir2pul(circuit, backend):
+    # transform quantum circuit to pulse schedule
+    with pulse.build(backend) as pulse_prog:
+        pulse.call(circuit)
+    return pulse_prog
+
+
+def snp(qubit, backend):
+    circuit = QuantumCircuit(qubit + 1)
+    circuit.h(qubit)
+    sched = cir2pul(circuit, backend)
+    sched = block_to_schedule(sched)
+    return sched
+
+
+def tnp(qubit, cqubit, backend):
+    circuit = QuantumCircuit(cqubit + 1)
+    circuit.cx(qubit, cqubit)
+    sched = cir2pul(circuit, backend)
+    sched = block_to_schedule(sched)
+    return sched
+
+
+def pul_append(sched1, sched2):
+    sched = sched1.append(sched2)
+    return sched
+
+
+def map_amp(pulse_ansatz, modified_list):
+    sched = Schedule()
+    for inst, amp in zip(
+        pulse_ansatz.filter(is_parametric_pulse).instructions, modified_list
+    ):
+        inst[1].pulse._amp = amp
+    for i in pulse_ansatz.instructions:
+        if is_parametric_pulse(i):
+            sched += copy.deepcopy(i[1])
+    return sched
+
+
+def get_from(d: dict, key: str):
+
+    value = 0
+    if key in d:
+        value = d[key]
+    return value
+
+
+def run_pulse_sim(measurement_pulse):
+    measure_result = []
+    for measure_pulse in measurement_pulse:
+        shots = 1024
+        pulse_sim = qiskit.providers.aer.PulseSimulator.from_backend(FakeJakarta())
+        pul_sim = assemble(
+            measure_pulse,
+            backend=pulse_sim,
+            shots=1024,
+            meas_level=2,
+            meas_return="single",
+        )
+        results = pulse_sim.run(pul_sim).result()
+
+        counts = results.get_counts()
+        expectation_value = (
+            (get_from(counts, "00") + get_from(counts, "11"))
+            - (get_from(counts, "10") + get_from(counts, "01"))
+        ) / shots
+        measure_result.append(expectation_value)
+    return measure_result
+
+
+def gen_LC(parameters_array):
+    dim_design = int(len(parameters_array))
+    Mid = int(len(parameters_array) / 2)
+    bound = np.ones((dim_design, 2)) * np.array([0, 0.9])
+    bound[-Mid:] = bound[-Mid:] * np.pi * 2
+    tol = 1e-3  # tolerance for optimization precision.
+    lb = bound[:, 0]
+    ub = bound[:, 1]
+    LC = LinearConstraint(np.eye(dim_design), lb, ub, keep_feasible=False)
+    return LC
+
+
+def observe_genearte(pulse_ansatz, backend):
+    qubits = 0, 1
+    with pulse.build(backend) as pulse_measurez0:
+        # z measurement of qubit 0 and 1
+        pulse.call(pulse_ansatz)
+        pulse.barrier(0, 1)
+        pulse.measure(0)
+    with pulse.build(backend) as pulse_measurez1:
+        # z measurement of qubit 0 and 1
+        pulse.call(pulse_ansatz)
+        pulse.barrier(0, 1)
+        pulse.measure(1)
+    with pulse.build(backend) as pulse_measurez:
+        # z measurement of qubit 0 and 1
+        pulse.call(pulse_ansatz)
+        pulse.barrier(0, 1)
+        pulse.measure(qubits)
+    with pulse.build(backend) as pulse_measurex:
+        # x measurement of qubit 0 and 1
+        pulse.call(pulse_ansatz)
+        pulse.barrier(0, 1)
+        pulse.u2(0, np.pi, 0)
+        pulse.u2(0, np.pi, 1)
+        pulse.barrier(0, 1)
+        pulse.measure(qubits)
+
+    with pulse.build(backend) as pulse_measurey:
+        # y measurement of qubit 0 and 1
+        pulse.call(pulse_ansatz)
+        pulse.barrier(0, 1)
+        pulse.u2(np.pi / 2, 0, 0)
+        pulse.u2(np.pi / 2, 0, 1)
+        pulse.barrier(0, 1)
+        pulse.measure(qubits)
+    measurement_pulse = [
+        pulse_measurez0,
+        pulse_measurez1,
+        pulse_measurez,
+        pulse_measurex,
+    ]
+    return measurement_pulse
diff --git a/torchquantum/pulse/templates/simplest_draft.py b/torchquantum/pulse/templates/simplest_draft.py
new file mode 100644
index 00000000..69d3bad3
--- /dev/null
+++ b/torchquantum/pulse/templates/simplest_draft.py
@@ -0,0 +1,70 @@
+import os
+import argparse
+import time
+import numpy as np
+
+import torch
+import torch.nn as nn
+import torch.optim as optim
+
+import torchquantum as tq
+
+from torchdiffeq import odeint
+
+
+# class SqaurePulse(Pulse):
+
+
+
+
+# class Pulse(tq.QuantumModule):
+#    def __init__():
+
+
+
+
+
+# class Solver(ABC):
+#    def __init__(
+#       self,
+#
+#
+#    ):
+
+
+z_value = np.array([[1,0],[0,-1]])
+z = torch.tensor(z_value,dtype=torch.complex64)
+
+x_value = np.array([[0,1],[1,0]])
+x = torch.tensor(x_value,dtype=torch.complex64)
+
+y0_value = np.array([1,0])
+y0 = torch.tensor(y0_value,dtype=torch.complex64)
+
+dt = 0.22 #ns
+# test 10 dt first: 2.2ns
+# torch.nn.parameter.Parameter
+pulse_value = torch.tensor([1,2,3,4,5,5,4,3,2,1]) / 10 #MHz
+pulse = torch.nn.parameter.Parameter(pulse_value)
+t_list = torch.tensor(list(range(10))) * dt
+
+assert pulse.size() == t_list.size()
+
+def H(t):
+    t_ind = (t/dt).long()
+    h = z + x * pulse[t_ind]
+    # print("current ind:",t_ind)
+    print("current h:",h)
+    return h
+
+def f(t, y):
+    h = H(t)
+    return -1.j * torch.matmul(h, y)
+y_t = odeint(f, y0, t_list)
+print("y0:",y0)
+print("y_t:",y_t)
+print("norm:",torch.norm(y_t,dim=1))
+
+# calculate the gradients of pulse segments
+# y_t[-1][0].real.backward()
+# print(pulse.grad)
diff --git a/torchquantum/pulse/utils.py b/torchquantum/pulse/utils.py
new file mode 100644
index 00000000..48c481e1
--- /dev/null
+++ b/torchquantum/pulse/utils.py
@@ -0,0 +1,17 @@
+import numpy as np
+import torch
+
+def sigmax():
+    x_value =  np.array([[0,1],[1,0]])
+    return torch.tensor(x_value,dtype=torch.complex64)
+
+
+def sigmay():
+    y_value = np.array([[0,-1.j],[1.j,0]])
+    return torch.tensor(y_value,dtype=torch.complex64)
+
+def sigmaz():
+    z_value = np.array([[1,0],[0,-1]])
+    return torch.tensor(z_value,dtype=torch.complex64)
+
+
diff --git a/torchquantum/pulse_utils.py b/torchquantum/pulse_utils.py
index bad2a9b5..eee206d1 100644
--- a/torchquantum/pulse_utils.py
+++ b/torchquantum/pulse_utils.py
@@ -38,6 +38,16 @@
 
 
 def is_parametric_pulse(t0, *inst: Union["Schedule", Instruction]):
+    """
+        Check if the instruction is a parametric pulse.
+
+        Args:
+            t0 (tuple): Tuple containing the time and instruction.
+            inst (tuple): Tuple containing the instruction.
+
+        Returns:
+            bool: True if the instruction is a parametric pulse, False otherwise.
+        """
     inst = t0[1]
     t0 = t0[0]
     if isinstance(inst, pulse.Play):
@@ -47,6 +57,15 @@ def is_parametric_pulse(t0, *inst: Union["Schedule", Instruction]):
 
 
 def extract_ampreal(pulse_prog):
+    """
+       Extract the real part of pulse amplitudes from the pulse program.
+
+       Args:
+           pulse_prog (Schedule): The pulse program.
+
+       Returns:
+           np.array: Array of real parts of pulse amplitudes.
+       """
     # extract the real part of pulse amplitude, igonred the imaginary part.
     amp_list = list(
         map(
@@ -60,6 +79,15 @@ def extract_ampreal(pulse_prog):
 
 
 def extract_amp(pulse_prog):
+    """
+        Extract the pulse amplitudes from the pulse program.
+
+        Args:
+            pulse_prog (Schedule): The pulse program.
+
+        Returns:
+            np.array: Array of pulse amplitudes.
+        """
     # extract the pulse amplitdue.
     amp_list = list(
         map(
@@ -79,6 +107,16 @@ def extract_amp(pulse_prog):
 
 
 def is_phase_pulse(t0, *inst: Union["Schedule", Instruction]):
+    """
+        Check if the instruction is a phase pulse.
+
+        Args:
+            t0 (tuple): Tuple containing the time and instruction.
+            inst (tuple): Tuple containing the instruction.
+
+        Returns:
+            bool: True if the instruction is a phase pulse, False otherwise.
+        """
     inst = t0[1]
     t0 = t0[0]
     if isinstance(inst, pulse.ShiftPhase):
@@ -87,6 +125,15 @@ def is_phase_pulse(t0, *inst: Union["Schedule", Instruction]):
 
 
 def extract_phase(pulse_prog):
+    """
+        Extract the phase values from the pulse program.
+
+        Args:
+            pulse_prog (Schedule): The pulse program.
+
+        Returns:
+            list: List of phase values.
+        """
 
     for _, ShiftPhase in pulse_prog.filter(is_phase_pulse).instructions:
         # print(play.pulse.amp)
@@ -103,6 +150,16 @@ def extract_phase(pulse_prog):
 
 
 def cir2pul(circuit, backend):
+    """
+        Transform a quantum circuit to a pulse schedule.
+
+        Args:
+            circuit (QuantumCircuit): The quantum circuit.
+            backend: The backend for the pulse schedule.
+
+        Returns:
+            Schedule: The pulse schedule.
+        """
     # transform quantum circuit to pulse schedule
     with pulse.build(backend) as pulse_prog:
         pulse.call(circuit)
@@ -110,6 +167,16 @@ def cir2pul(circuit, backend):
 
 
 def snp(qubit, backend):
+    """
+        Create a Schedule for the simultaneous z measurement of a qubit and a control qubit.
+
+        Args:
+            qubit (int): The target qubit.
+            backend: The backend for the pulse schedule.
+
+        Returns:
+            Schedule: The pulse schedule for simultaneous z measurement.
+        """
     circuit = QuantumCircuit(qubit + 1)
     circuit.h(qubit)
     sched = cir2pul(circuit, backend)
@@ -118,6 +185,17 @@ def snp(qubit, backend):
 
 
 def tnp(qubit, cqubit, backend):
+    """
+        Create a Schedule for the simultaneous controlled-x measurement of a qubit and a control qubit.
+
+        Args:
+            qubit (int): The target qubit.
+            cqubit (int): The control qubit.
+            backend: The backend for the pulse schedule.
+
+        Returns:
+            Schedule: The pulse schedule for simultaneous controlled-x measurement.
+        """
     circuit = QuantumCircuit(cqubit + 1)
     circuit.cx(qubit, cqubit)
     sched = cir2pul(circuit, backend)
@@ -126,11 +204,31 @@ def tnp(qubit, cqubit, backend):
 
 
 def pul_append(sched1, sched2):
+    """
+        Append two pulse schedules.
+
+        Args:
+            sched1 (Schedule): The first pulse schedule.
+            sched2 (Schedule): The second pulse schedule.
+
+        Returns:
+            Schedule: The combined pulse schedule.
+        """
     sched = sched1.append(sched2)
     return sched
 
 
 def map_amp(pulse_ansatz, modified_list):
+    """
+        Map modified pulse amplitudes to the pulse ansatz.
+
+        Args:
+            pulse_ansatz (Schedule): The pulse ansatz.
+            modified_list (list): List of modified pulse amplitudes.
+
+        Returns:
+            Schedule: The pulse schedule with modified amplitudes.
+        """
     sched = Schedule()
     for inst, amp in zip(
         pulse_ansatz.filter(is_parametric_pulse).instructions, modified_list
@@ -151,6 +249,15 @@ def get_from(d: dict, key: str):
 
 
 def run_pulse_sim(measurement_pulse):
+    """
+        Run pulse simulations for the given measurement pulses.
+
+        Args:
+            measurement_pulse (list): List of measurement pulses.
+
+        Returns:
+            list: List of measurement results.
+        """
     measure_result = []
     for measure_pulse in measurement_pulse:
         shots = 1024
@@ -174,6 +281,15 @@ def run_pulse_sim(measurement_pulse):
 
 
 def gen_LC(parameters_array):
+    """
+        Generate linear constraints for the optimization.
+
+        Args:
+            parameters_array (np.array): Array of parameters.
+
+        Returns:
+            LinearConstraint: Linear constraint for the optimization.
+        """
     dim_design = int(len(parameters_array))
     Mid = int(len(parameters_array) / 2)
     bound = np.ones((dim_design, 2)) * np.array([0, 0.9])
@@ -186,6 +302,16 @@ def gen_LC(parameters_array):
 
 
 def observe_genearte(pulse_ansatz, backend):
+    """
+        Generate measurement pulses for observing the pulse ansatz.
+
+        Args:
+            pulse_ansatz (Schedule): The pulse ansatz.
+            backend: The backend for the pulse schedule.
+
+        Returns:
+            list: List of measurement pulses.
+        """
     qubits = 0, 1
     with pulse.build(backend) as pulse_measurez0:
         # z measurement of qubit 0 and 1
diff --git a/torchquantum/quantization/clifford_quantization.py b/torchquantum/quantization/clifford_quantization.py
index e4f43cbe..3e159d5f 100644
--- a/torchquantum/quantization/clifford_quantization.py
+++ b/torchquantum/quantization/clifford_quantization.py
@@ -6,14 +6,44 @@
 
 
 class QuantizeFunction(torch.autograd.Function):
+    """A custom quantization function for use in the backward pass of CliffordQuantizer.
+
+    Methods:
+        forward(ctx, x): Forward pass of the quantization function.
+
+        backward(ctx, grad_output): Backward pass of the quantization function.
+    """
+    
     @staticmethod
     def forward(ctx: Any, x: torch.Tensor) -> Any:
+        """
+        Forward pass of the quantization function.
+
+        Args:
+            ctx (Any): Context object.
+            x (torch.Tensor): Input tensor.
+
+        Returns:
+            Any: Quantized output tensor.
+        """
+        
         # should be round so that the changes would be small, values close to
         # 2pi should go to 2pi
         return x.round()
 
     @staticmethod
     def backward(ctx: Any, grad_output: Any) -> Any:
+        """
+        Backward pass of the quantization function.
+
+        Args:
+            ctx (Any): Context object.
+            grad_output (Any): Gradient of the output tensor.
+
+        Returns:
+            Any: Gradient of the input tensor.
+        """
+        
         grad_input = grad_output.clone()
         mean, std = grad_input.mean(), grad_input.std()
         return grad_input.clamp_(mean - 3 * std, mean + 3 * std)
@@ -26,6 +56,16 @@ def __init__(self):
     # straight-through estimator
     @staticmethod
     def quantize_sse(params):
+        """
+        Apply straight-through estimator quantization to the given parameters.
+
+        Args:
+            params (torch.Tensor): Input parameters.
+
+        Returns:
+            torch.Tensor: Quantized parameters.
+        """
+        
         param = params[0][0]
         param = param % (2 * np.pi)
         param = np.pi / 2 * QuantizeFunction.apply(param / (np.pi / 2))
diff --git a/torchquantum/super_utils.py b/torchquantum/super_utils.py
index 5565a264..7014e7b9 100644
--- a/torchquantum/super_utils.py
+++ b/torchquantum/super_utils.py
@@ -6,6 +6,44 @@
 
 
 class ArchSampler(object):
+    """
+        Architecture sampler for a neural network model.
+
+        This class provides methods to sample architectures for a neural network model.
+        It supports different sampling strategies, such as plain random sampling,
+        limiting differences between consecutive architectures, progressive sampling,
+        and more.
+
+        Args:
+            model (nn.Module): The neural network model for which architectures will be sampled.
+            strategy (dict): The sampling strategy configuration.
+            n_layers_per_block (int): The number of layers per block (if the architecture is block-based).
+
+        Attributes:
+            model (nn.Module): The neural network model.
+            total_steps (int): The total number of steps.
+            arch_space (list): The architecture space.
+            strategy (dict): The sampling strategy.
+            sample_arch_old (list): The previous sampled architecture.
+            arch_space_stage (None): The architecture space stage.
+            n_ops_smallest (int): The smallest number of operations in the space.
+            n_ops_largest (int): The largest number of operations in the space.
+            is_block_based (bool): Indicates if the architecture is block-based.
+            n_layers_per_block (int): The number of layers per block.
+            n_ops_per_chunk (list): The number of operations per chunk.
+            sample_n_ops (int): The number of operations in the sampled architecture.
+            current_stage (int): The current stage in progressive sampling.
+            current_chunk (int): The current chunk in progressive sampling.
+            step (int): The current step.
+
+        Methods:
+            set_total_steps(total_steps): Sets the total number of steps.
+            get_n_ops_per_chunk(): Separates the space into several subspaces based on the strategy.
+            get_sample_stats(sample_arch): Calculates the number of operations in a sample architecture.
+            get_space_stats(): Calculates the max and smallest number of operations in the space.
+            get_random_sample_arch(): Generates a random sample architecture.
+            get_uniform_sample_arch(): Generates a sample architecture based on the sampling strategy.
+        """
     def __init__(self, model: nn.Module, strategy=None, n_layers_per_block=None):
         self.model = model
         self.total_steps = None
diff --git a/torchquantum/utils.py b/torchquantum/utils.py
index 2f7f9310..6f93d997 100644
--- a/torchquantum/utils.py
+++ b/torchquantum/utils.py
@@ -50,6 +50,7 @@
     "tensor_form",
     "matrix_form",
     "dm_to_mixture_of_state",
+    "parameter_shift_gradient",
 ]
 
 
@@ -71,6 +72,15 @@ def pauli_eigs(n) -> np.ndarray:
 
 
 def diag(x):
+    """
+        Compute the diagonal matrix from a given input tensor.
+
+        Args:
+            x (torch.Tensor): Input tensor.
+
+        Returns:
+            torch.Tensor: Diagonal matrix with the diagonal elements from the input tensor.
+        """
     # input tensor, output tensor with diagonal as the input
     # manual implementation because torch.diag does not support autograd of
     # complex number
@@ -84,6 +94,21 @@ def diag(x):
 
 
 class Timer(object):
+    """
+       Timer class to measure the execution time of a code block.
+
+       Args:
+           device (str): Device to use for timing. Can be "gpu" or "cpu".
+           name (str): Name of the task being measured.
+           times (int): Number of times the task will be executed.
+
+       Example:
+           # Measure the execution time of a code block on the GPU
+           with Timer(device="gpu", name="MyTask", times=100):
+               # Code block to be measured
+               ...
+
+       """
     def __init__(self, device="gpu", name="", times=100):
         self.device = device
         self.name = name
@@ -107,6 +132,21 @@ def __exit__(self, exc_type, exc_value, tb):
 
 
 def get_unitary_loss(model: nn.Module):
+    """
+        Calculate the unitary loss of a model.
+
+        The unitary loss measures the deviation of the trainable unitary matrices
+        in the model from the identity matrix.
+
+        Args:
+            model (nn.Module): The model containing trainable unitary matrices.
+
+        Returns:
+            torch.Tensor: The unitary loss.
+
+        Example:
+            loss = get_unitary_loss(model)
+        """
     loss = 0
     for name, params in model.named_parameters():
         if "TrainableUnitary" in name:
@@ -121,6 +161,22 @@ def get_unitary_loss(model: nn.Module):
 
 
 def legalize_unitary(model: nn.Module):
+    """
+        Legalize the unitary matrices in the model.
+
+        The function modifies the trainable unitary matrices in the model by applying
+        singular value decomposition (SVD) and reassembling the matrices using the
+        reconstructed singular values.
+
+        Args:
+            model (nn.Module): The model containing trainable unitary matrices.
+
+        Returns:
+            None
+
+        Example:
+            legalize_unitary(model)
+        """
     with torch.no_grad():
         for name, params in model.named_parameters():
             if "TrainableUnitary" in name:
@@ -130,6 +186,23 @@ def legalize_unitary(model: nn.Module):
 
 
 def switch_little_big_endian_matrix(mat):
+    """
+        Switches the little-endian and big-endian order of a multi-dimensional matrix.
+
+        The function reshapes the input matrix to a 2D or multi-dimensional matrix with dimensions
+        that are powers of 2. It then switches the order of the dimensions, effectively changing
+        the little-endian order to big-endian, or vice versa. The function can handle both
+        batched and non-batched matrices.
+
+        Args:
+            mat (numpy.ndarray): The input matrix.
+
+        Returns:
+            numpy.ndarray: The matrix with the switched endian order.
+
+        Example:
+            switched_mat = switch_little_big_endian_matrix(mat)
+        """
     if len(mat.shape) % 2 == 1:
         is_batch_matrix = True
         bsz = mat.shape[0]
@@ -152,6 +225,27 @@ def switch_little_big_endian_matrix(mat):
 
 
 def switch_little_big_endian_state(state):
+    """
+       Switches the little-endian and big-endian order of a quantum state vector.
+
+       The function reshapes the input state vector to a 1D or multi-dimensional state vector with
+       dimensions that are powers of 2. It then switches the order of the dimensions, effectively
+       changing the little-endian order to big-endian, or vice versa. The function can handle both
+       batched and non-batched state vectors.
+
+       Args:
+           state (numpy.ndarray): The input state vector.
+
+       Returns:
+           numpy.ndarray: The state vector with the switched endian order.
+
+       Raises:
+           ValueError: If the dimension of the state vector is not 1 or 2.
+
+       Example:
+           switched_state = switch_little_big_endian_state(state)
+       """
+
     if len(state.shape) > 1:
         is_batch_state = True
         bsz = state.shape[0]
@@ -190,6 +284,26 @@ def switch_little_big_endian_state_test():
 
 
 def get_expectations_from_counts(counts, n_wires):
+    """
+       Calculate expectation values from counts.
+
+       This function takes a counts dictionary or a list of counts dictionaries
+       and calculates the expectation values based on the probability of measuring
+       the state '1' on each wire. The expectation values are computed as the
+       flipped difference between the probability of measuring '1' and the probability
+       of measuring '0' on each wire.
+
+       Args:
+           counts (dict or list[dict]): The counts dictionary or a list of counts dictionaries.
+           n_wires (int): The number of wires.
+
+       Returns:
+           numpy.ndarray: The expectation values.
+
+       Example:
+           counts = {'000': 10, '100': 5, '010': 15}
+           expectations = get_expectations_from_counts(counts, 3)
+       """
     exps = []
     if isinstance(counts, dict):
         counts = [counts]
@@ -209,6 +323,30 @@ def get_expectations_from_counts(counts, n_wires):
 
 
 def find_global_phase(mat1, mat2, threshold):
+    """
+        Find a numerical stable global phase between two matrices.
+
+        This function compares the elements of two matrices `mat1` and `mat2`
+        and identifies a numerical stable global phase by finding the first
+        non-zero element pair with absolute values greater than the specified
+        threshold. The global phase is calculated as the ratio of the corresponding
+        elements in `mat2` and `mat1`.
+
+        Args:
+            mat1 (numpy.ndarray): The first matrix.
+            mat2 (numpy.ndarray): The second matrix.
+            threshold (float): The threshold for identifying non-zero elements.
+
+        Returns:
+            float or None: The global phase ratio if a numerical stable phase is found,
+                None otherwise.
+
+        Example:
+            mat1 = np.array([[1+2j, 0+1j], [0-1j, 2+3j]])
+            mat2 = np.array([[2+4j, 0+2j], [0-2j, 4+6j]])
+            threshold = 0.5
+            global_phase = find_global_phase(mat1, mat2, threshold)
+        """
     for i in range(mat1.shape[0]):
         for j in range(mat1.shape[1]):
             # find a numerical stable global phase
@@ -274,6 +412,36 @@ def build_module_op_list(m: QuantumModule, x=None) -> List:
 def build_module_from_op_list(
     op_list: List[Dict], remove_ops=False, thres=None
 ) -> QuantumModule:
+    """
+       Build a quantum module from an operation list.
+
+       This function takes an operation list, which contains dictionaries representing
+       quantum operations, and constructs a quantum module from those operations.
+       The module can optionally remove operations based on certain criteria, such as
+       low parameter values. The removed operations can be counted and logged.
+
+       Args:
+           op_list (List[Dict]): The operation list, where each dictionary represents
+               an operation with keys: "name", "has_params", "trainable", "wires",
+               "n_wires", and "params".
+           remove_ops (bool): Whether to remove operations based on certain criteria.
+               Defaults to False.
+           thres (float): The threshold for removing operations. If a parameter value
+               is smaller in absolute value than this threshold, the corresponding
+               operation is removed. Defaults to None, in which case a threshold of
+               1e-5 is used.
+
+       Returns:
+           QuantumModule: The constructed quantum module.
+
+       Example:
+           op_list = [
+               {"name": "RX", "has_params": True, "trainable": True, "wires": [0], "n_wires": 2, "params": [0.5]},
+               {"name": "CNOT", "has_params": False, "trainable": False, "wires": [0, 1], "n_wires": 2, "params": None},
+               {"name": "RY", "has_params": True, "trainable": True, "wires": [1], "n_wires": 2, "params": [1.2]},
+           ]
+           module = build_module_from_op_list(op_list, remove_ops=True, thres=0.1)
+       """
     logger.info(f"Building module from op_list...")
     thres = 1e-5 if thres is None else thres
     n_removed_ops = 0
@@ -312,6 +480,32 @@ def build_module_from_op_list(
 
 
 def build_module_description_test():
+    """
+        Test function for building module descriptions.
+
+        This function demonstrates the usage of `build_module_op_list` and `build_module_from_op_list`
+        functions to build module descriptions and create quantum modules from those descriptions.
+
+        Example:
+            import pdb
+            from torchquantum.plugins import tq2qiskit
+            from examples.core.models.q_models import QFCModel12
+
+            pdb.set_trace()
+            q_model = QFCModel12({"n_blocks": 4})
+            desc = build_module_op_list(q_model.q_layer)
+            print(desc)
+            q_dev = tq.QuantumDevice(n_wires=4)
+            m = build_module_from_op_list(desc)
+            tq2qiskit(q_dev, m, draw=True)
+
+            desc = build_module_op_list(
+                tq.RandomLayerAllTypes(n_ops=200, wires=[0, 1, 2, 3], qiskit_compatible=True)
+            )
+            print(desc)
+            m1 = build_module_from_op_list(desc)
+            tq2qiskit(q_dev, m1, draw=True)
+        """
     import pdb
 
     from torchquantum.plugins import tq2qiskit
@@ -410,7 +604,17 @@ def get_v_c_reg_mapping(circ):
 
 
 def get_cared_configs(conf, mode) -> Config:
-    """only preserve cared configs"""
+    """
+        Get the relevant configurations based on the mode.
+
+        Args:
+            conf (Config): The configuration object.
+            mode (str): The mode indicating the desired configuration.
+
+        Returns:
+            Config: The modified configuration object with only the relevant configurations preserved.
+        """
+
     conf = copy.deepcopy(conf)
     ignores = [
         "callbacks",
@@ -476,6 +680,16 @@ def get_cared_configs(conf, mode) -> Config:
 
 
 def get_success_rate(properties, transpiled_circ):
+    """
+        Estimate the success rate of a transpiled quantum circuit.
+
+        Args:
+            properties (list): List of gate error properties.
+            transpiled_circ (QuantumCircuit): The transpiled quantum circuit.
+
+        Returns:
+            float: The estimated success rate.
+        """
     # estimate the success rate according to the error rates of single and
     # two-qubit gates in transpiled circuits
 
@@ -501,6 +715,16 @@ def get_success_rate(properties, transpiled_circ):
 
 
 def get_provider(backend_name, hub=None):
+    """
+        Get the provider object for a specific backend from IBM Quantum.
+
+        Args:
+            backend_name (str): Name of the backend.
+            hub (str): Optional hub name.
+
+        Returns:
+            IBMQProvider: The provider object.
+        """
     # mass-inst-tech-1 or MIT-1
     if backend_name in ["ibmq_casablanca", "ibmq_rome", "ibmq_bogota", "ibmq_jakarta"]:
         if hub == "mass" or hub is None:
@@ -551,6 +775,15 @@ def get_provider_hub_group_project(hub="ibm-q", group="open", project="main"):
 
 
 def normalize_statevector(states):
+    """
+       Normalize a statevector to ensure the square magnitude of the statevector sums to 1.
+
+       Args:
+           states (torch.Tensor): The statevector tensor.
+
+       Returns:
+           torch.Tensor: The normalized statevector tensor.
+       """
     # make sure the square magnitude of statevector sum to 1
     # states = states.contiguous()
     original_shape = states.shape
@@ -711,6 +944,23 @@ def dm_to_mixture_of_state(dm: torch.Tensor, atol=1e-10):
 
 
 def partial_trace_test():
+    """
+        Test function for performing partial trace on a quantum device.
+
+        This function demonstrates how to use the `partial_trace` function from `torchquantum.functional`
+        to perform partial trace on a quantum device.
+
+        The function applies Hadamard gate on the first qubit and a CNOT gate between the first and second qubits.
+        Then, it performs partial trace on the first qubit and converts the resulting density matrices into
+        mixtures of states.
+
+        Prints the resulting mixture of states.
+
+        Note: This function assumes that you have already imported the necessary modules and functions.
+
+        Returns:
+            None
+        """
     import torchquantum.functional as tqf
 
     n_wires = 4
@@ -730,3 +980,64 @@ def partial_trace_test():
     build_module_description_test()
     switch_little_big_endian_matrix_test()
     switch_little_big_endian_state_test()
+
+
+def parameter_shift_gradient(model, input_data, expectation_operator, shift_rate=np.pi*0.5, shots=1024):
+    ''' 
+    This function calculates the gradient of a parametrized circuit using the parameter shift rule to be fed into 
+    a classical optimizer, its formula is given by 
+    gradient for the ith parameter =( expectation_value(the_ith_parameter + shift_rate)-expectation_value(the_ith_parameter - shift_rate) ) *0.5 
+    Args: 
+        model(tq.QuantumModule): the model that you want to use, which includes the quantum device and the parameters
+        input(torch.tensor): the input data that you are using
+        expectation_operator(str): the observable that you want to calculate the expectation value of, usually the Z operator 
+        (i.e 'ZZZ' for 3 qubits or 3 wires)
+        shift_rate(float , optional): the rate that you would like to shift the parameter with at every iteration, by default pi*0.5
+        shots(int , optional): the number of shots to use per parameter ,(for 10 parameters and 1024 shots = 10240 shots in total)
+        by default = 1024.
+    Returns:
+        torch.tensor : An array of the gradients of all the parameters in the circuit. 
+    '''
+    par_num = []
+    for p in model.parameters():#since the model.parameters() Returns an iterator over module parameters,to get the number of parameter i have to iterate over all of them
+        par_num.append(p)
+    gradient_of_par = torch.zeros(len(par_num))
+                                        
+    def clone_model(model_to_clone):#i have to note:this clone_model function was made with GPT 
+        cloned_model = type(model_to_clone)()  # Create a new instance of the same class
+        cloned_model.load_state_dict(model_to_clone.state_dict())  # Copy the state dictionary
+        return cloned_model
+
+    # Clone the models
+    model_plus_shift  = clone_model(model)
+    model_minus_shift = clone_model(model)
+    state_dict_plus_shift  = model_plus_shift.state_dict()
+    state_dict_minus_shift = model_minus_shift.state_dict()
+    #####################
+
+    for idx, key in enumerate(state_dict_plus_shift):
+        if idx < 2:  # Skip the first two keys because they are not paramters
+            continue
+        state_dict_plus_shift[key]  +=  shift_rate
+        state_dict_minus_shift[key] -=  shift_rate
+        
+        model_plus_shift.load_state_dict(state_dict_plus_shift  )
+        model_minus_shift.load_state_dict(state_dict_minus_shift)
+        
+        model_plus_shift.forward(input_data)
+        model_minus_shift.forward(input_data)
+        
+        state_dict_plus_shift  = model_plus_shift.state_dict()
+        state_dict_minus_shift = model_minus_shift.state_dict()
+                
+        
+        
+        expectation_plus_shift = tq.expval_joint_sampling(model_plus_shift.q_device, observable=expectation_operator, n_shots=shots)
+        expectation_minus_shift = tq.expval_joint_sampling(model_minus_shift.q_device, observable=expectation_operator, n_shots=shots)
+
+
+        state_dict_plus_shift[key]  -=  shift_rate
+        state_dict_minus_shift[key] +=  shift_rate
+        
+        gradient_of_par[idx-2] = (expectation_plus_shift - expectation_minus_shift) * 0.5
+    return gradient_of_par
\ No newline at end of file
diff --git a/torchquantum/vqe_utils.py b/torchquantum/vqe_utils.py
index 5abbc171..87d0a7b9 100644
--- a/torchquantum/vqe_utils.py
+++ b/torchquantum/vqe_utils.py
@@ -3,6 +3,16 @@
 
 
 def parse_hamiltonian_file(filename: str) -> dict:
+    """
+       Parses a Hamiltonian file and returns the Hamiltonian information as a dictionary.
+
+       Args:
+           filename (str): The name of the Hamiltonian file.
+
+       Returns:
+           dict: A dictionary containing the Hamiltonian information.
+
+       """
     hamil_info = {}
     with open(filename, "r") as rfid:
         lines = rfid.read().split("\n")
@@ -32,6 +42,21 @@ def parse_hamiltonian_file(filename: str) -> dict:
 
 
 def generate_n_hamiltonian(n_wires: int, n_hamil: int, n_lines: int) -> dict:
+    """
+        Generates a random Hamiltonian with a specified number of wires and terms.
+
+        Args:
+            n_wires (int): The number of wires.
+            n_hamil (int): The number of terms in the Hamiltonian.
+            n_lines (int): The desired number of unique Hamiltonians to generate.
+
+        Returns:
+            dict: A dictionary containing the generated Hamiltonian information.
+
+        Raises:
+            AssertionError: If n_hamil is not within the range (0, n_wires].
+
+        """
     assert 0 < n_hamil <= n_wires
 
     hamil_info = {
@@ -63,11 +88,16 @@ def generate_n_hamiltonian(n_wires: int, n_hamil: int, n_lines: int) -> dict:
 
 
 def test_parse_hamiltonian_file():
+    """
+        Test function for parse_hamiltonian_file.
+        """
     file = "../examples/vqe/h2.txt"
     print(parse_hamiltonian_file(file))
 
 
-def test_generate_n_hamiltonian():
+def test_generate_n_hamiltonian():"""
+    Test function for generate_n_hamiltonian.
+    """
     print(generate_n_hamiltonian(n_wires=5, n_hamil=3, n_lines=100))
 
 


Test patch:
diff --git a/test/operators/test_op.py b/test/operators/test_op.py
index c0fd2eac..b70d9f53 100644
--- a/test/operators/test_op.py
+++ b/test/operators/test_op.py
@@ -9,6 +9,8 @@
 from tqdm import tqdm
 
 import qiskit.circuit.library.standard_gates as qiskit_gate
+import qiskit.circuit.library as qiskit_library
+
 
 RND_TIMES = 100
 
@@ -49,6 +51,21 @@
     # {'qiskit': qiskit_gate.?, 'tq': tq.CU2},
     {"qiskit": qiskit_gate.CU3Gate, "tq": tq.CU3},
     {"qiskit": qiskit_gate.ECRGate, "tq": tq.ECR},
+    {"qiskit": qiskit_library.QFT, "tq": tq.QFT},
+    {"qiskit": qiskit_gate.SdgGate, "tq": tq.SDG},
+    {"qiskit": qiskit_gate.TDgGate, "tq": tq.TDG},
+    {"qiskit": qiskit_gate.SXdgGate, "tq": tq.SXDG},
+    {"qiskit": qiskit_gate.CHGate, "tq": tq.CH},
+    {"qiskit": qiskit_gate.CCZGate, "tq": tq.CCZ},
+    {"qiskit": qiskit_gate.iSwapGate, "tq": tq.ISWAP},
+    {"qiskit": qiskit_gate.CSGate, "tq": tq.CS},
+    {"qiskit": qiskit_gate.CSdgGate, "tq": tq.CSDG},
+    {"qiskit": qiskit_gate.CSXGate, "tq": tq.CSX},
+    {"qiskit": qiskit_gate.DCXGate, "tq": tq.DCX},
+    {'qiskit': qiskit_gate.XXMinusYYGate, 'tq': tq.XXMINYY},
+    {'qiskit': qiskit_gate.XXPlusYYGate, 'tq': tq.XXPLUSYY},
+    {"qiskit": qiskit_gate.C3XGate, "tq": tq.C3X},
+    {"qiskit": qiskit_gate.RGate, "tq": tq.R},
 ]
 
 import os