From c749d01b5e7472ae0954ea0b87ca83f3d73ffba7 Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 3 Sep 2025 15:49:59 +0300 Subject: [PATCH 1/5] Integrate the Qiskit plugin with the Qiskit transpiler --- synpy/pyproject.toml | 5 ++++- synpy/python/synpy/qiskit/plugin.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/synpy/pyproject.toml b/synpy/pyproject.toml index 49ddb394..cb40a5c1 100644 --- a/synpy/pyproject.toml +++ b/synpy/pyproject.toml @@ -36,4 +36,7 @@ target-version = "py312" select = ["E", "F", "W"] [tool.ruff.format] -quote-style = "double" \ No newline at end of file +quote-style = "double" + +[project.entry-points."qiskit.synthesis"] +"clifford.synpy" = "synpy.qiskit.plugin:SynPyCliffordPlugin" \ No newline at end of file diff --git a/synpy/python/synpy/qiskit/plugin.py b/synpy/python/synpy/qiskit/plugin.py index d41ac09a..385cd9e6 100644 --- a/synpy/python/synpy/qiskit/plugin.py +++ b/synpy/python/synpy/qiskit/plugin.py @@ -11,7 +11,7 @@ class SynPyCliffordPlugin(HighLevelSynthesisPlugin): def __init__(self) -> None: super().__init__() - def run(self, clifford: Clifford, coupling_map: CouplingMap, target: Target, qubits: list) -> QuantumCircuit: + def run(self, clifford: Clifford, coupling_map: CouplingMap, target: Target, qubits: list, **options) -> QuantumCircuit: n = clifford.num_qubits tableau_x = clifford.tableau[:, :n] tableau_z = clifford.tableau[:, n : 2 * n] From 15e102d52b1db5f102e2a20e21ea150e7408f37b Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 3 Sep 2025 15:50:14 +0300 Subject: [PATCH 2/5] Ignore notebook checkpoints --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4a6e33d8..991b8b78 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ target/ # Ideas .idea/ .vscode/ + +# Jupyter notebook checkpoints +examples/.ipynb_checkpoints/ From 87518761f14dc19ba53f8c5e6a116bf6c137baec Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 3 Sep 2025 15:51:05 +0300 Subject: [PATCH 3/5] Basic user guide --- README.md | 7 +- examples/Basic python usage.ipynb | 348 +++++++++++++++++++ examples/Rust library overview.ipynb | 500 +++++++++++++++++++++++++++ requirements.in | 1 + synpy/Cargo.lock | 42 ++- 5 files changed, 895 insertions(+), 3 deletions(-) create mode 100644 examples/Basic python usage.ipynb create mode 100644 examples/Rust library overview.ipynb diff --git a/README.md b/README.md index 17dbd169..4ec19a36 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,9 @@ Clean: ``` make clean -``` \ No newline at end of file +``` + +## Usage +You can find a basic description of the rust package and how to use it [here](examples/Rust%20library%20overview.ipynb). This is mainly meant for contributors and integrators. + +If you want to use the algorithms provided in the library to compile your circuits, you can find a simple user guide for the Python bindings [here](examples/Basic%20python%20usage.ipynb). \ No newline at end of file diff --git a/examples/Basic python usage.ipynb b/examples/Basic python usage.ipynb new file mode 100644 index 00000000..e1b64c49 --- /dev/null +++ b/examples/Basic python usage.ipynb @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "79cab848", + "metadata": {}, + "source": [ + "# Introductory python tutorial for the synIR library\n", + "Welcome to the synIR (/ˈsɪnɚ/) library!\n", + "\n", + "This jupyter notebook showcases the basic python usage of our quantum compilation library.\n", + "\n", + "You can find a detailed detailed explanation of the rust-part of the library in [Rust library overview notebook](Rust%20library%20overview.ipynb)." + ] + }, + { + "cell_type": "markdown", + "id": "8cb6ca94", + "metadata": {}, + "source": [ + "## Installing synpy library\n", + "If you install from the top github folder\n", + "```\n", + "pip install requirements.txt\n", + "pip install synpy/ \n", + "```\n", + "\n", + "If you install from the pypi\n", + "```\n", + "pip install synpy\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "fa8b2c58", + "metadata": {}, + "source": [ + "## Using Qiskit Highlevel synthesis pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bb3f555", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.quantum_info import Clifford\n", + "from qiskit import QuantumCircuit" + ] + }, + { + "cell_type": "markdown", + "id": "1eeb1e13", + "metadata": {}, + "source": [ + "Create a simple Qiskit Clifford circuit. Unfortuntately, a Qiskit QuantumCircuit does not know that it is Clifford so we need to make construct it first (`cliff`) and then add it to the a circuit (`qc`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4ee235d", + "metadata": {}, + "outputs": [], + "source": [ + "tmp = QuantumCircuit(2)\n", + "tmp.h(0)\n", + "tmp.cx(0, 1)\n", + "cliff = Clifford(tmp)\n", + "qc = QuantumCircuit(2)\n", + "qc.append(cliff, (0,1))" + ] + }, + { + "cell_type": "markdown", + "id": "41b04321", + "metadata": {}, + "source": [ + "Synthesize the circuit directly using the plugin. Here, the plugin takes the Qiskit Clifford object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb6a5001", + "metadata": {}, + "outputs": [], + "source": [ + "from synpy.qiskit.plugin import SynPyCliffordPlugin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dfc5427a", + "metadata": {}, + "outputs": [], + "source": [ + "direct_circ = SynPyCliffordPlugin().run(cliff, None, None, [])\n", + "print(direct_circ)" + ] + }, + { + "cell_type": "markdown", + "id": "e09456ee", + "metadata": {}, + "source": [ + "Using the Qiskit transpiler by giving the plugin to the transpiler." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a974bc82", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig\n", + "from qiskit import transpile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47657c94", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌───┐ \n", + "q_0: ┤ H ├──■──\n", + " └───┘┌─┴─┐\n", + "q_1: ─────┤ X ├\n", + " └───┘\n" + ] + } + ], + "source": [ + "hls_config = HLSConfig(clifford=[SynPyCliffordPlugin()])\n", + "transpiled_circuit = transpile(qc, hls_config=hls_config)\n", + "print(transpiled_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "5972832d", + "metadata": {}, + "source": [ + "Our plugin is also registered as a string in the known transpiler plugins as `\"synpy\"` so we can use that instead. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f4ff80de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['synpy', 'ag', 'bm', 'default', 'greedy', 'layers', 'lnn']" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis import (\n", + " high_level_synthesis_plugin_names,\n", + ")\n", + " \n", + "high_level_synthesis_plugin_names(\"clifford\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78798b35", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌───┐ \n", + "q_0: ┤ H ├──■──\n", + " └───┘┌─┴─┐\n", + "q_1: ─────┤ X ├\n", + " └───┘\n" + ] + } + ], + "source": [ + "hls_config = HLSConfig(clifford=[\"synpy\"])\n", + "transpiled_circuit = transpile(qc, hls_config=hls_config)\n", + "print(transpiled_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "31011de6", + "metadata": {}, + "source": [ + "## Using Pauliopt library with synpy" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "14ef5e61", + "metadata": {}, + "outputs": [], + "source": [ + "from pauliopt.pauli.pauli_polynomial import PauliPolynomial\n", + "from pauliopt.pauli.pauli_gadget import PPhase\n", + "from pauliopt.pauli_strings import X, Y, Z, I\n", + "from pauliopt.utils import Angle, pi" + ] + }, + { + "cell_type": "markdown", + "id": "6a0b91d4", + "metadata": {}, + "source": [ + "Make a Toffoli gate using PauliOpt" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "12340f3d", + "metadata": {}, + "outputs": [], + "source": [ + "pp = PauliPolynomial(3)\n", + "angle = pi/4\n", + "pp >>= PPhase(angle) @ [I, I, X]\n", + "pp >>= PPhase(angle) @ [Z, I, I]\n", + "pp >>= PPhase(angle) @ [I, Z, I]\n", + "pp >>= PPhase(-angle) @ [I, Z, X]\n", + "pp >>= PPhase(-angle) @ [Z, Z, I]\n", + "pp >>= PPhase(-angle) @ [Z, I, X]\n", + "pp >>= PPhase(angle) @ [Z, Z, X]" + ] + }, + { + "cell_type": "markdown", + "id": "d42f7096", + "metadata": {}, + "source": [ + "Synthesize a new circuit using synpy" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8d3c8807", + "metadata": {}, + "outputs": [], + "source": [ + "from synpy.pauliopt.synthesizer import PauliOptSynthesizer" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "78c8e3f4", + "metadata": {}, + "outputs": [], + "source": [ + "pauliopt_circuit = PauliOptSynthesizer().synthesize(pp)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9ceaabaa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌─────────┐ »\n", + "q_0: ┤ Rz(π/4) ├─────────────────────■────────────────────────────■───────────»\n", + " ├─────────┤ ┌─┴─┐ ┌──────────┐ ┌─┴─┐ »\n", + "q_1: ┤ Rz(π/4) ├─────────────■─────┤ X ├────┤ Rz(7π/4) ├──■─────┤ X ├──────■──»\n", + " └──┬───┬──┘┌─────────┐┌─┴─┐┌──┴───┴───┐└──────────┘┌─┴─┐┌──┴───┴───┐┌─┴─┐»\n", + "q_2: ───┤ H ├───┤ Rz(π/4) ├┤ X ├┤ Rz(7π/4) ├────────────┤ X ├┤ Rz(7π/4) ├┤ X ├»\n", + " └───┘ └─────────┘└───┘└──────────┘ └───┘└──────────┘└───┘»\n", + "« \n", + "«q_0: ─────────────■────────────\n", + "« │ \n", + "«q_1: ─────────────┼────■───────\n", + "« ┌─────────┐┌─┴─┐┌─┴─┐┌───┐\n", + "«q_2: ┤ Rz(π/4) ├┤ X ├┤ X ├┤ H ├\n", + "« └─────────┘└───┘└───┘└───┘\n" + ] + } + ], + "source": [ + "print(pauliopt_circuit.to_qiskit())" + ] + }, + { + "cell_type": "markdown", + "id": "5ee0dfac", + "metadata": {}, + "source": [ + "# Future plans\n", + "This library is still under construction. If you are looking for specific features that are not yet implemented, please make an issue on the Github page.\n", + "\n", + "Our next plans include, but are not limited to:\n", + "- Implement the PSGS algorithm for Pauli Polynomial synthesis (open PR)\n", + "- Improve Qiskit transpiler integration, e.g. add PauliExponentialSynthesis\n", + "- Circuit-to-PauliExponential parser for generic circuits\n", + "- Improve code structure, package structure, and code style." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "synpy", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/Rust library overview.ipynb b/examples/Rust library overview.ipynb new file mode 100644 index 00000000..232454ba --- /dev/null +++ b/examples/Rust library overview.ipynb @@ -0,0 +1,500 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "995cd1ee-bbe3-485f-9f7f-0e080aeb56b8", + "metadata": {}, + "source": [ + "# Introductory tutorial for the synIR library and its python bindings\n", + "Welcome to the synIR (/ˈsɪnɚ/) library!\n", + "\n", + "This jupyter notebook showcases the basic usage of our quantum compilation library." + ] + }, + { + "cell_type": "markdown", + "id": "1a866fba", + "metadata": {}, + "source": [ + "## Library overview\n", + "This compilation library is a rust-based library with python bindings. \n", + "\n", + "synIR stands for SYNthesizable Intermediate Representation. Unlike gate-based intermediate representations (e.g. QIR), the library represents quantum circuits as sequences of exponentiated Paulistrings and a Clifford tableau $A$: \n", + "\n", + "$$U = A\\Pi (\\Pi_{P \\in \\{I, X, Y, Z\\}^N} e^{-\\alpha P})$$\n", + "\n", + "Here, the inner product of $e^{-\\alpha P}$ is assumed to be mutually commuting. This assumption helps optimize circuits resulting from a trotterization process in exchange for a potential increase of the Trotter Error." + ] + }, + { + "cell_type": "markdown", + "id": "936dcffd", + "metadata": {}, + "source": [ + "### Lexicon\n", + "Since there are some conflicting terminology in literature, we will define what we mean by some of these terms here in alphabetical order.\n", + "\n", + "**Pauli Exponential** is the universal representation that we use in the library, it contains a Clifford tableau and a sequence of Pauli Polynomials. An exponential is a product and since the product of matrices is not commutative, the sequences in the Pauli Exponential do not commute.\n", + "\n", + "**Pauliletter** is the letter by which we refer to any of the four Pauli matrices: $I$, $X$, $Y$, and $Z$.\n", + "\n", + "**Pauli Polynomial** is the subsequence of Paulistrings in the Pauli Exponential that we assume to be mutually commuting. An example of this is the Phase Polynomial, which is a Pauli Polynomial consisting only of I and Z Pauliletters. The naming scheme is analogous to the Phase Polynomial. Similarly, a polynomial contains a sum of terms and since the sum of matrices is commutative, the terms in the Pauli Polynomial should also commute.\n", + "\n", + "**Paulistring** is a sequence of Pauli letters representing the tensor product of the respective Pauli matrices.\n" + ] + }, + { + "cell_type": "markdown", + "id": "39e8ad17", + "metadata": {}, + "source": [ + "## Basic data structures" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8450a288", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + ":dep syn = { path = \"../.\" } // If you run the syn package locally" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38e416d0", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use syn::data_structures::{PauliString, PauliPolynomial, CliffordTableau}\n", + "use syn::ir::pauli_exponential::PauliExponential" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ccbfc999-681a-4083-96a8-9b8b35fa4ade", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "I X Y Z\n" + ] + } + ], + "source": [ + "let pauli_string = \"IXYZ\";\n", + "let paulivec = PauliString::from_text(pauli_string);\n", + "\n", + "println!(\"{}\", paulivec);" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4987d78f", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pauli Polynomial size:4 qubits x 3 terms\n", + " | 0.3 0.7 0.12 | \n", + "QB0 | I X Y |\n", + "QB1 | X X Y |\n", + "QB2 | Y I I |\n", + "QB3 | Z I I |\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let ham = vec![(\"IXYZ\", 0.3), (\"XXII\", 0.7), (\"YYII\", 0.12)];\n", + "let pp = PauliPolynomial::from_hamiltonian(ham);\n", + "//println!(\"{}\", pp); // WIP\n", + "\n", + "println!(\"Pauli Polynomial size:{} qubits x {} terms\", pp.size(), pp.length());\n", + "\n", + "print!(\" | \");\n", + "for i in 0..pp.length(){\n", + " print!(\"{} \", pp.angle(i));\n", + "}\n", + "println!(\" | \");\n", + "for i in 0..pp.size(){\n", + " println!(\"QB{} | {} |\", i, pp.chains()[i]);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2b395afb", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use syn::data_structures::PropagateClifford; // Would be nice if this was not needed when importing CliffordTableau" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2ae4557a", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CliffordTableau(4)\n", + "X I I I Z I I I\n", + "I X I I I Z I I\n", + "I I X I I I Z I\n", + "I I I X I I I Z\n", + "+ + + + + + + +\n", + "\n", + "After an V gate:\n", + "CliffordTableau(4)\n", + "X I I I Z I I I\n", + "I X I I I Y I I\n", + "I I X I I I Z I\n", + "I I I X I I I Z\n", + "+ + + + + - + +\n", + "\n", + "After an V gate:\n", + "CliffordTableau(4)\n", + "X I I I Z I I I\n", + "I X I I I Z I I\n", + "I I X I I I Z I\n", + "I I I X I I I Z\n", + "+ + + + + - + +\n" + ] + } + ], + "source": [ + "let mut ct = CliffordTableau::new(4);\n", + "println!(\"{}\", ct);\n", + "ct.v(1);\n", + "\n", + "println!(\"\\nAfter an V gate:\\n{}\", ct);\n", + "ct.v(1);\n", + "\n", + "println!(\"\\nAfter an V gate:\\n{}\", ct);" + ] + }, + { + "cell_type": "markdown", + "id": "0a42392d", + "metadata": {}, + "source": [ + "## A Toffoli gate example" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "58e7f80f", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let angles = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25, 0.25];\n", + "let mut ccz_ham = vec![\"ZII\", \"IZI\", \"IIZ\", \"ZZI\", \"ZIZ\", \"IZZ\", \"ZZZ\"];\n", + "let mut ccz_pp = PauliPolynomial::from_hamiltonian(ccz_ham.iter().copied().zip(angles).collect());\n", + "let mut ccz_ct = CliffordTableau::new(3);\n", + "ccz_ct.h(2); // Add the final H around the CCZ\n", + "// Add the first H before the CCZ\n", + "ccz_pp.h(2);\n", + "ccz_ct.h(2);\n", + "let toffoli = PauliExponential::new(std::collections::VecDeque::from([ccz_pp]), ccz_ct); \n", + "//toffoli.h(2); // This does not work yet, so we do this to the separate parts, so we did this earlier.\n", + "//toffoli // Does not work yet before visualization" + ] + }, + { + "cell_type": "markdown", + "id": "9b1bb2df", + "metadata": {}, + "source": [ + "### Define a circuit class because our rust code does not have one" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "05fc5625", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use syn::ir::{CliffordGates, Gates};" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "92c93083", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "type Angle = f64;\n", + "#[derive(Debug, Default)]\n", + "pub struct MockCircuit {\n", + " commands: Vec,\n", + "}\n", + "\n", + "#[derive(Debug, PartialEq)]\n", + "pub enum MockCommand {\n", + " CX(syn::IndexType, syn::IndexType),\n", + " CZ(syn::IndexType, syn::IndexType),\n", + " X(syn::IndexType),\n", + " Y(syn::IndexType),\n", + " Z(syn::IndexType),\n", + " H(syn::IndexType),\n", + " S(syn::IndexType),\n", + " V(syn::IndexType),\n", + " SDgr(syn::IndexType),\n", + " VDgr(syn::IndexType),\n", + " Rx(syn::IndexType, Angle),\n", + " Ry(syn::IndexType, Angle),\n", + " Rz(syn::IndexType, Angle),\n", + "}\n", + "\n", + "impl MockCircuit {\n", + " pub fn new() -> Self {\n", + " Self {\n", + " commands: Vec::new(),\n", + " }\n", + " }\n", + " pub fn commands(&self) -> &Vec {\n", + " &self.commands\n", + " }\n", + "}\n", + "\n", + "impl CliffordGates for MockCircuit {\n", + " fn s(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::S(target));\n", + " }\n", + "\n", + " fn v(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::V(target));\n", + " }\n", + "\n", + " fn s_dgr(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::SDgr(target));\n", + " }\n", + "\n", + " fn v_dgr(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::VDgr(target));\n", + " }\n", + "\n", + " fn x(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::X(target));\n", + " }\n", + "\n", + " fn y(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::Y(target));\n", + " }\n", + "\n", + " fn z(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::Z(target));\n", + " }\n", + "\n", + " fn h(&mut self, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::H(target));\n", + " }\n", + "\n", + " fn cx(&mut self, control: syn::IndexType, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::CX(control, target));\n", + " }\n", + "\n", + " fn cz(&mut self, control: syn::IndexType, target: syn::IndexType) {\n", + " self.commands.push(MockCommand::CZ(control, target));\n", + " }\n", + "}\n", + "\n", + "impl Gates for MockCircuit {\n", + " fn rx(&mut self, target: syn::IndexType, angle: Angle) {\n", + " self.commands.push(MockCommand::Rx(target, angle));\n", + " }\n", + "\n", + " fn ry(&mut self, target: syn::IndexType, angle: Angle) {\n", + " self.commands.push(MockCommand::Ry(target, angle));\n", + " }\n", + "\n", + " fn rz(&mut self, target: syn::IndexType, angle: Angle) {\n", + " self.commands.push(MockCommand::Rz(target, angle));\n", + " }\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "id": "89e1ef50", + "metadata": {}, + "source": [ + "### Synthesize the circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ced385fa", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use syn::ir::pauli_exponential::PauliExponentialSynthesizer;\n", + "use syn::ir::clifford_tableau::CliffordTableauSynthStrategy;\n", + "use syn::ir::pauli_polynomial::PauliPolynomialSynthStrategy;\n", + "use syn::ir::Synthesizer;" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4afc9c94", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "MockCircuit { commands: [Rz(0, 0.25), Rz(1, 0.25), H(2), Rz(2, 0.25), CX(0, 1), Rz(1, -0.25), CX(0, 2), Rz(2, -0.25), CX(1, 2), Rz(2, -0.25), CX(0, 2), Rz(2, 0.25), CX(0, 1), CX(0, 2), CX(1, 2), H(2)] }" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut circuit = MockCircuit::new();\n", + "let mut synthesizer = PauliExponentialSynthesizer::from_strategy(\n", + " PauliPolynomialSynthStrategy::Naive,\n", + " CliffordTableauSynthStrategy::Naive,\n", + ");\n", + "synthesizer.synthesize(toffoli, &mut circuit);\n", + "circuit" + ] + }, + { + "cell_type": "markdown", + "id": "9500bf03", + "metadata": {}, + "source": [ + "## FAQ\n", + "\n", + "### How do I make a circuit?\n", + "You don't. \n", + "\n", + "The rust part of the library does not have a notion of quantum circuit and this is by design. The only data structure that contains what the quantum program does is our intermediate representation. That intermediate representation can consume gates and generate gates in a way that is defined by the `PropagateClifford`, `MaskedPropagateClifford`, `Synthesizer`, and `AdjointSynthesizer` traits.\n", + "\n", + "Circuits are a concept that are practically needed for integration, so we translate circuit classes from external libraries directly to our intermediate representation without relyin on an internal definition for a circuit. " + ] + }, + { + "cell_type": "markdown", + "id": "f1ed838a", + "metadata": {}, + "source": [ + "# Future plans\n", + "This library is still under construction. If you are looking for specific features that are not yet implemented, please make an issue on the Github page.\n", + "\n", + "Our next plans include, but are not limited to:\n", + "- Implement the PSGS algorithm for Pauli Polynomial synthesis\n", + "- Improve Qiskit transpiler integration\n", + "- Circuit-to-PauliExponential parser for generic circuits\n", + "- Improve code structure, package structure, and code style" + ] + }, + { + "cell_type": "markdown", + "id": "95877799", + "metadata": {}, + "source": [ + "# Continue reading\n", + "i.e. references\n", + "\n", + "1. Huang, Q., Winderl, D., Meijer-Van De Griend, A., & Yeung, R. (2024, September). Redefining Lexicographical Ordering: Optimizing Pauli String Decompositions for Quantum Compiling. In 2024 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 885-896). IEEE. [PDF](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=10821336)\n", + "1. Winderl, D., Huang, Q., de Griend, A. M. V., & Yeung, R. (2023). Architecture-aware synthesis of stabilizer circuits from clifford tableaus. arXiv preprint arXiv:2309.08972. [PDF](https://arxiv.org/pdf/2309.08972)\n", + "1. Meijer–van de Griend, A. (2024). Advances in Quantum Compilation in the NISQ Era (Doctoral dissertation, Doctoral dissertation, University of Helsinki). [PDF](http://hdl.handle.net/10138/569455)\n", + "1. Meijer-van de Griend, A. (2025). A comparison of quantum compilers using a DAG-based or phase polynomial-based intermediate representation. Journal of Systems and Software, 221, 112224. [PDF](https://www.sciencedirect.com/science/article/pii/S0164121224002681)\n", + "1. Meijer-Van De Griend, A. (2024, March). The Quantum Circuit Model is not a Practical Representation of Quantum Software. In 2024 IEEE International Conference on Software Analysis, Evolution and Reengineering-Companion (SANER-C) (pp. 146-148). IEEE. [PDF](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=10621734)\n", + "1. Cowtan, A., Dilkes, S., Duncan, R., Simmons, W., & Sivarajah, S. (2019). Phase gadget synthesis for shallow circuits. arXiv preprint arXiv:1906.01734. [PDF](https://arxiv.org/pdf/1906.01734)\n", + "1. Perkkola, F., Salmeperä, I., de Griend, A. M. V., Wang, C. C. J., Bennink, R. S., & Nurminen, J. K. (2025). Optimizing State Preparation for Variational Quantum Regression on NISQ Hardware. arXiv preprint arXiv:2505.17713. [PDF](https://arxiv.org/pdf/2505.17713)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/requirements.in b/requirements.in index 1d827548..e3cb12a4 100644 --- a/requirements.in +++ b/requirements.in @@ -3,3 +3,4 @@ pip-tools pytest qiskit bandit +pauliopt \ No newline at end of file diff --git a/synpy/Cargo.lock b/synpy/Cargo.lock index e173122f..a54a01ed 100644 --- a/synpy/Cargo.lock +++ b/synpy/Cargo.lock @@ -44,6 +44,12 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "funty" version = "2.0.0" @@ -55,6 +61,9 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "heck" @@ -110,11 +119,13 @@ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "petgraph" -version = "0.7.1" -source = "git+https://github.com/daehiff/petgraph?branch=add-mst-prim#705c658dd87d9f571453cff90871e1153d6f96fa" +version = "0.8.2" +source = "git+https://github.com/keefehuang/petgraph#1cdbae4cecd2fb6591697eb8cba3d2c9a76c438e" dependencies = [ "fixedbitset", + "hashbrown", "indexmap", + "serde", ] [[package]] @@ -210,6 +221,26 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.99", +] + [[package]] name = "syn" version = "0.1.0" @@ -217,6 +248,7 @@ dependencies = [ "bitvec", "itertools", "petgraph", + "typenum", ] [[package]] @@ -251,6 +283,12 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.18" From 00a5881f395b9bbed752fc295a190aaaacd3709c Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 3 Sep 2025 15:53:29 +0300 Subject: [PATCH 4/5] Update outputs --- examples/Basic python usage.ipynb | 53 ++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/examples/Basic python usage.ipynb b/examples/Basic python usage.ipynb index e1b64c49..d46a1623 100644 --- a/examples/Basic python usage.ipynb +++ b/examples/Basic python usage.ipynb @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "9bb3f555", "metadata": {}, "outputs": [], @@ -60,10 +60,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "d4ee235d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "tmp = QuantumCircuit(2)\n", "tmp.h(0)\n", @@ -83,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "eb6a5001", "metadata": {}, "outputs": [], @@ -93,10 +104,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "dfc5427a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌───┐ \n", + "q_0: ┤ H ├──■──\n", + " └───┘┌─┴─┐\n", + "q_1: ─────┤ X ├\n", + " └───┘\n" + ] + } + ], "source": [ "direct_circ = SynPyCliffordPlugin().run(cliff, None, None, [])\n", "print(direct_circ)" @@ -123,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "47657c94", "metadata": {}, "outputs": [ @@ -155,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "f4ff80de", "metadata": {}, "outputs": [ @@ -165,7 +188,7 @@ "['synpy', 'ag', 'bm', 'default', 'greedy', 'layers', 'lnn']" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -180,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "78798b35", "metadata": {}, "outputs": [ @@ -212,7 +235,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, "id": "14ef5e61", "metadata": {}, "outputs": [], @@ -233,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "id": "12340f3d", "metadata": {}, "outputs": [], @@ -259,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "id": "8d3c8807", "metadata": {}, "outputs": [], @@ -269,7 +292,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "id": "78c8e3f4", "metadata": {}, "outputs": [], @@ -279,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 13, "id": "9ceaabaa", "metadata": {}, "outputs": [ From 8f679d72308fed4c5b89e5facd30d34db90af5fa Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 3 Sep 2025 16:07:47 +0300 Subject: [PATCH 5/5] mypy --- synpy/python/synpy/qiskit/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/synpy/python/synpy/qiskit/plugin.py b/synpy/python/synpy/qiskit/plugin.py index 385cd9e6..522fc81d 100644 --- a/synpy/python/synpy/qiskit/plugin.py +++ b/synpy/python/synpy/qiskit/plugin.py @@ -1,3 +1,5 @@ +from typing import Any + from qiskit.transpiler import CouplingMap, Target from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin from qiskit.quantum_info import Clifford @@ -11,7 +13,7 @@ class SynPyCliffordPlugin(HighLevelSynthesisPlugin): def __init__(self) -> None: super().__init__() - def run(self, clifford: Clifford, coupling_map: CouplingMap, target: Target, qubits: list, **options) -> QuantumCircuit: + def run(self, clifford: Clifford, coupling_map: CouplingMap, target: Target, qubits: list, **options: Any) -> QuantumCircuit: n = clifford.num_qubits tableau_x = clifford.tableau[:, :n] tableau_z = clifford.tableau[:, n : 2 * n]