diff --git a/.github/workflows/cov.yml b/.github/workflows/cov.yml index 184e038e0..3853f999e 100644 --- a/.github/workflows/cov.yml +++ b/.github/workflows/cov.yml @@ -22,7 +22,7 @@ jobs: run: python -m pip install --upgrade pip - name: Install graphix with dev deps. - run: pip install .[dev] + run: pip install .[dev,extra] - name: Run pytest run: pytest --cov=./graphix --cov-report=xml --cov-report=term diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 9547c58b8..0e66dc704 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -31,7 +31,7 @@ jobs: run: python -m pip install --upgrade pip - name: Install requirements - run: pip install -r requirements.txt -r requirements-dev.txt -r docs/requirements.txt + run: pip install .[dev,doc] - name: Make docs - run: make -C docs html SPHINXOPTS="--fail-on-warning" + run: sphinx-build -M html docs/source docs/build --fail-on-warning diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index c105a9045..92736df27 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -25,7 +25,7 @@ jobs: - run: | python -m pip install --upgrade pip - pip install -e .[dev] + pip install -e .[dev,extra] - run: mypy diff --git a/.readthedocs.yaml b/.readthedocs.yaml index e4481b896..eb1aa776a 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,15 +6,17 @@ version: 2 build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: - python: "3.10" + python: "3.12" sphinx: configuration: docs/source/conf.py python: install: - - requirements: docs/requirements.txt + - requirements: requirements.txt + - requirements: requirements-extra.txt + - requirements: requirements-doc.txt - method: pip path: . diff --git a/benchmarks/graphsim.py b/benchmarks/graphsim.py deleted file mode 100644 index 9f6921e2a..000000000 --- a/benchmarks/graphsim.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -Graph state simulator backends -======================================= - -Here we benchmark our graph state simulator for MBQC with different backends. - -Currently, we have two backends: `networkx `_ -and `rustworkx `_. -Both Python packages are used to manipulate graphs. -While networkx is a pure Python package, rustworkx is a Rust package with Python bindings, which is faster than networkx. -""" - -# %% -# Firstly, let us import relevant modules: - -from copy import copy -from time import perf_counter - -import matplotlib.pyplot as plt -import numpy as np - -from graphix import Circuit - -# %% -# Next, define a function to generate random Clifford circuits. - - -def genpair(n_qubits, count, rng): - pairs = [] - for _ in range(count): - choice = [j for j in range(n_qubits)] - x = rng.choice(choice) - choice.pop(x) - y = rng.choice(choice) - pairs.append((x, y)) - return pairs - - -def random_clifford_circuit(nqubits, depth, seed=42): - rng = np.random.default_rng(seed) - circuit = Circuit(nqubits) - gate_choice = list(range(5)) - for _ in range(depth): - for j, k in genpair(nqubits, 2, rng): - circuit.cnot(j, k) - for j in range(nqubits): - k = rng.choice(gate_choice) - if k == 0: # H - circuit.h(j) - elif k == 1: # S - circuit.s(j) - elif k == 2: # X - circuit.x(j) - elif k == 3: # Z - circuit.z(j) - elif k == 4: # Y - circuit.y(j) - else: - pass - return circuit - - -# %% -# We generate a set of random Clifford circuits with different widths. - -DEPTH = 3 -test_cases = [i for i in range(2, 300, 10)] -graphix_patterns = {} - -for i in test_cases: - circuit = random_clifford_circuit(i, DEPTH) - pattern = circuit.transpile() - pattern.standardize() - pattern.shift_signals() - nodes, edges = pattern.get_graph() - graphix_patterns[i] = (circuit, pattern, len(nodes)) - - -# %% -# We then run simulations. -# First, we run the pattern optimization using networkx. -networkx_time = [] -networkx_node = [] - -for width, (_, pattern, num_nodes) in graphix_patterns.items(): - pattern_copy = copy(pattern) - start = perf_counter() - pattern_copy.perform_pauli_measurements() - end = perf_counter() - networkx_node.append(num_nodes) - print(f"width: {width}, number of nodes: {num_nodes}, depth: {DEPTH}, time: {end - start}") - networkx_time.append(end - start) - - -# %% -# Next, we run the pattern optimization using rustworkx. -rustworkx_time = [] -rustworkx_node = [] - -for width, (_, pattern, num_nodes) in graphix_patterns.items(): - pattern_copy = copy(pattern) - start = perf_counter() - pattern_copy.perform_pauli_measurements(use_rustworkx=True) - end = perf_counter() - rustworkx_node.append(num_nodes) - print(f"width: {width}, number of nodes: {num_nodes}, depth: {DEPTH}, time: {end - start}") - rustworkx_time.append(end - start) - -# %% -# Lastly, we compare the simulation times. -assert networkx_node == rustworkx_node - -fig = plt.figure() -ax = fig.add_subplot(111) - -ax.scatter(networkx_node, networkx_time, label="networkx", color="blue") -ax.scatter(rustworkx_node, rustworkx_time, label="rustworkx", color="red") -ax.set( - xlabel="Number of nodes in the graph state", - xscale="log", - ylabel="Time (s)", - yscale="log", - title="Time to perform Pauli measurements on the graph state", -) -ax.legend() -fig.show() - -# %% -# Performing pattern optimization using rustworkx is slightly faster than networkx. - -import importlib.metadata # noqa: E402 - -# print package versions. -for pkg in ["graphix", "networkx", "rustworkx"]: - print(f"{pkg} - {importlib.metadata.version(pkg)}") diff --git a/benchmarks/readme.rst b/benchmarks/readme.rst deleted file mode 100644 index 068c3a629..000000000 --- a/benchmarks/readme.rst +++ /dev/null @@ -1,4 +0,0 @@ -Graphix benchmarks -================== - -Here are some benchmark of Graphix and related packages. diff --git a/benchmarks/requirements.txt b/benchmarks/requirements.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/benchmarks/statevec.py b/benchmarks/statevec.py deleted file mode 100644 index 200c04562..000000000 --- a/benchmarks/statevec.py +++ /dev/null @@ -1,169 +0,0 @@ -""" -Statevector simulation of MBQC patterns -======================================= - -Here we benchmark our statevector simulator for MBQC. - -The methods and modules we use are the followings: - 1. :meth:`graphix.pattern.Pattern.simulate_pattern` - Pattern simulator with statevector backend. - 2. :mod:`paddle_quantum.mbqc` - Pattern simulation using :mod:`paddle_quantum.mbqc`. -""" - -# %% -# Firstly, let us import relevant modules: - -from time import perf_counter - -import matplotlib.pyplot as plt -import numpy as np -from paddle import to_tensor -from paddle_quantum.mbqc.qobject import Circuit as PaddleCircuit -from paddle_quantum.mbqc.simulator import MBQC as PaddleMBQC # noqa: N811 -from paddle_quantum.mbqc.transpiler import transpile as paddle_transpile - -from graphix import Circuit - -rng = np.random.default_rng() - -# %% -# Next, define a circuit to be transpiled into measurement pattern: - - -def simple_random_circuit(nqubit, depth): - r"""Generate a test circuit for benchmarking. - - This function generates a circuit with nqubit qubits and depth layers, - having layers of CNOT and Rz gates with random placements. - - Parameters - ---------- - nqubit : int - number of qubits - depth : int - number of layers - - Returns - ------- - circuit : graphix.transpiler.Circuit object - generated circuit - """ - qubit_index = [i for i in range(nqubit)] - circuit = Circuit(nqubit) - for _ in range(depth): - rng.shuffle(qubit_index) - for j in range(len(qubit_index) // 2): - circuit.cnot(qubit_index[2 * j], qubit_index[2 * j + 1]) - for j in range(len(qubit_index)): - circuit.rz(qubit_index[j], 2 * np.pi * rng.random()) - return circuit - - -# %% -# We define the test cases: shallow (depth=1) random circuits, only changing the number of qubits. - -DEPTH = 1 -test_cases = [i for i in range(2, 22)] -graphix_circuits = {} - -pattern_time = [] -circuit_time = [] - -# %% -# We then run simulations. -# First, we run the pattern simulation using `graphix`. -# For reference, we perform simple statevector simulation of the original gate network. -# Since transpilation into MBQC involves a significant increase in qubit number, -# the MBQC simulation is inherently slower as we will see. - -for width in test_cases: - circuit = simple_random_circuit(width, DEPTH) - graphix_circuits[width] = circuit - pattern = circuit.transpile() - pattern.standardize() - pattern.minimize_space() - nodes, edges = pattern.get_graph() - nqubit = len(nodes) - start = perf_counter() - pattern.simulate_pattern(max_qubit_num=30) - end = perf_counter() - print(f"width: {width}, nqubit: {nqubit}, depth: {DEPTH}, time: {end - start}") - pattern_time.append(end - start) - start = perf_counter() - circuit.simulate_statevector() - end = perf_counter() - circuit_time.append(end - start) - - -# %% -# Here we benchmark `paddle_quantum`, using the same original gate network and use `paddle_quantum.mbqc` module -# to transpile into a measurement pattern. - - -def translate_graphix_rc_into_paddle_quantum_circuit(graphix_circuit: Circuit) -> PaddleCircuit: - """Translate graphix circuit into paddle_quantum circuit. - - Parameters - ---------- - graphix_circuit : Circuit - graphix circuit - - Returns - ------- - paddle_quantum_circuit : PaddleCircuit - paddle_quantum circuit - """ - paddle_quantum_circuit = PaddleCircuit(graphix_circuit.width) - for instr in graphix_circuit.instruction: - if instr.name == "CNOT": - paddle_quantum_circuit.cnot(which_qubits=instr[1]) - elif instr.name == "RZ": - paddle_quantum_circuit.rz(which_qubit=instr[1], theta=to_tensor(instr[2], dtype="float64")) - return paddle_quantum_circuit - - -test_cases_for_paddle_quantum = [i for i in range(2, 22)] -paddle_quantum_time = [] - -for width in test_cases_for_paddle_quantum: - graphix_circuit = graphix_circuits[width] - paddle_quantum_circuit = translate_graphix_rc_into_paddle_quantum_circuit(graphix_circuit) - pat = paddle_transpile(paddle_quantum_circuit) - mbqc = PaddleMBQC() - mbqc.set_pattern(pat) - start = perf_counter() - mbqc.run_pattern() - end = perf_counter() - paddle_quantum_time.append(end - start) - - print(f"width: {width}, depth: {DEPTH}, time: {end - start}") - -# %% -# Lastly, we compare the simulation times. - -fig = plt.figure() -ax = fig.add_subplot(111) - -ax.scatter( - test_cases, circuit_time, label="direct statevector sim of original gate-based circuit (reference)", marker="x" -) -ax.scatter(test_cases, pattern_time, label="graphix pattern simulator") -ax.scatter(test_cases_for_paddle_quantum, paddle_quantum_time, label="paddle_quantum pattern simulator") -ax.set( - xlabel="Width of the original circuit", - ylabel="time (s)", - yscale="log", - title="Time to simulate random circuits", -) -fig.legend(bbox_to_anchor=(0.85, 0.9)) -fig.show() - -# %% -# MBQC simulation is a lot slower than the simulation of original gate network, since the number of qubit involved -# is significantly larger. - -import importlib.metadata # noqa: E402 - -# print package versions. -[print(f"{pkg} - {importlib.metadata.version(pkg)}") for pkg in ["numpy", "graphix", "paddlepaddle", "paddle-quantum"]] diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 4f518e626..000000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -sphinx>=5.0 -sphinx-gallery==0.11.1 -furo -sphinxawesome-theme==5.1.6 -numpy -graphix -matplotlib -scikit-learn -seaborn -ipython - --r ../benchmarks/requirements.txt diff --git a/docs/source/conf.py b/docs/source/conf.py index 7e49705eb..0b8d0f19b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,6 @@ "sphinx.ext.autosectionlabel", "sphinx.ext.napoleon", "sphinx_gallery.gen_gallery", - "sphinxawesome_theme.highlighting", ] templates_path = ["_templates"] diff --git a/noxfile.py b/noxfile.py index 508ba024f..6f32db51f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -17,5 +17,5 @@ def tests_minimal(session: Session) -> None: @nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12"]) def tests(session: Session) -> None: """Run the test suite with full dependencies.""" - session.install("-e", ".[dev]") + session.install("-e", ".[extra]") session.run("pytest") diff --git a/pyproject.toml b/pyproject.toml index 2030082d7..e160c1efb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = { file = ["requirements.txt"] } [tool.setuptools.dynamic.optional-dependencies] dev = { file = ["requirements-dev.txt"] } +doc = { file = ["requirements-doc.txt"] } extra = { file = ["requirements-extra.txt"] } [tool.ruff] diff --git a/requirements-dev.txt b/requirements-dev.txt index 98cbbc64a..9a82ef588 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,11 +14,3 @@ psutil pytest pytest-cov pytest-mock - -# Optional dependencies -qiskit>=1.0 -qiskit-aer -rustworkx - -# Optional dependency. Pinned due to version changes often being incompatible -pyzx==0.8.0 diff --git a/requirements-doc.txt b/requirements-doc.txt new file mode 100644 index 000000000..394a2341c --- /dev/null +++ b/requirements-doc.txt @@ -0,0 +1,7 @@ +furo +ipython +matplotlib +scikit-learn +seaborn +sphinx-gallery +sphinx>=5.0 diff --git a/requirements-extra.txt b/requirements-extra.txt index a87155897..3304cd229 100644 --- a/requirements-extra.txt +++ b/requirements-extra.txt @@ -1,2 +1,6 @@ -graphix-ibmq -graphix-perceval +# graphix-ibmq +# graphix-perceval +pyzx==0.8.0 +qiskit-aer +qiskit>=1.0 +rustworkx diff --git a/requirements.txt b/requirements.txt index e84c035be..b1e63b312 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -autoray>=0.6.0 eval_type_backport galois>=0.3.0 matplotlib