pyrauli 0.3.1__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyrauli-0.3.1 → pyrauli-0.4.0}/CMakeLists.txt +2 -1
- {pyrauli-0.3.1 → pyrauli-0.4.0}/PKG-INFO +6 -2
- {pyrauli-0.3.1 → pyrauli-0.4.0}/README.md +1 -1
- pyrauli-0.4.0/benchmarks/test_benchmark_qaoa.py +43 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/guides/how_to_qiskit.rst +14 -0
- pyrauli-0.4.0/docs/guides/how_to_symbolic.rst +99 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/index.rst +1 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/pyproject.toml +7 -2
- {pyrauli-0.3.1 → pyrauli-0.4.0}/src/pyrauli/__init__.py +17 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/src/pyrauli/_core/bindings.cpp +333 -7
- {pyrauli-0.3.1 → pyrauli-0.4.0}/src/pyrauli/backend.py +2 -2
- {pyrauli-0.3.1 → pyrauli-0.4.0}/src/pyrauli/estimator.py +96 -32
- pyrauli-0.4.0/tests/snippets/test_symbolic_circuit_noise.py +22 -0
- pyrauli-0.4.0/tests/snippets/test_symbolic_circuit_snippet.py +19 -0
- pyrauli-0.4.0/tests/snippets/test_symbolic_coefficient_guide.py +53 -0
- pyrauli-0.4.0/tests/snippets/test_symbolic_observable_guide.py +46 -0
- pyrauli-0.4.0/tests/snippets/test_symbolic_sympy.py +24 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_backend.py +48 -2
- pyrauli-0.4.0/tests/test_symbolic.py +12 -0
- pyrauli-0.4.0/tests/test_symbolic_circuit.py +61 -0
- pyrauli-0.4.0/tests/test_symbolic_coefficient.py +76 -0
- pyrauli-0.4.0/tests/test_symbolic_observable.py +88 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.clang-format +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.github/workflows/benchmark.yml +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.github/workflows/ci.yml +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.github/workflows/doc.yml +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.github/workflows/pypi.yml +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/.gitignore +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/LICENSE +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/benchmarks/test_benchmark_circuit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/benchmarks/test_benchmark_observable.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/benchmarks/test_benchmark_qiskit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/.gitignore +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/Makefile +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/_static/.gitkeep +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/_templates/.gitkeep +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/conf.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/explanation/theory.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/guides/how_to_circuit.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/guides/how_to_complexity.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/guides/how_to_noise.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/guides/how_to_observables.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/make.bat +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/reference/api.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/requirements.txt +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/docs/tutorials/getting_started.rst +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/examples/qiskit_backend.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/src/pyrauli/converters.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/snippets/test_basic_circuit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/snippets/test_observable_evolution.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/snippets/test_qiskit_backend_usage.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/snippets/test_readme.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_circuit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_circuit_qiskit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_noise_model.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_observable.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_observable_qiskit.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_pauli.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_policies.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_pyrauli.py +0 -0
- {pyrauli-0.3.1 → pyrauli-0.4.0}/tests/test_truncator.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pyrauli
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A very fast and easy to use Quantum circuit simulator relying on Pauli propagation. Compatible with qiskit.
|
5
5
|
License: GNU GENERAL PUBLIC LICENSE
|
6
6
|
Version 3, 29 June 2007
|
@@ -686,11 +686,15 @@ Project-URL: Homepage, https://github.com/zefresk/pyrauli
|
|
686
686
|
Requires-Python: >=3.9
|
687
687
|
Provides-Extra: qiskit
|
688
688
|
Requires-Dist: qiskit>=2.0.0; extra == "qiskit"
|
689
|
+
Requires-Dist: numpy; extra == "qiskit"
|
690
|
+
Provides-Extra: symbolic
|
691
|
+
Requires-Dist: sympy; extra == "symbolic"
|
689
692
|
Provides-Extra: test
|
690
693
|
Requires-Dist: pytest>=8.0.0; extra == "test"
|
691
694
|
Requires-Dist: pytest-cov>=5.0.0; extra == "test"
|
692
695
|
Requires-Dist: pytest-benchmark; extra == "test"
|
693
696
|
Requires-Dist: numpy; extra == "test"
|
697
|
+
Requires-Dist: sympy; extra == "test"
|
694
698
|
Description-Content-Type: text/markdown
|
695
699
|
|
696
700
|
# pyrauli: High-Performance Quantum Circuit Simulation
|
@@ -753,7 +757,7 @@ final_observable = circuit.run(observable)
|
|
753
757
|
|
754
758
|
# 5. Retrieve the final expectation value
|
755
759
|
# The expectation value is calculated with respect to the initial |00...0> state
|
756
|
-
expectation_value = final_observable.
|
760
|
+
expectation_value = final_observable.expectation_value()
|
757
761
|
|
758
762
|
print(f"Final observable: {final_observable}")
|
759
763
|
print(f"Expectation value: {expectation_value}")
|
@@ -58,7 +58,7 @@ final_observable = circuit.run(observable)
|
|
58
58
|
|
59
59
|
# 5. Retrieve the final expectation value
|
60
60
|
# The expectation value is calculated with respect to the initial |00...0> state
|
61
|
-
expectation_value = final_observable.
|
61
|
+
expectation_value = final_observable.expectation_value()
|
62
62
|
|
63
63
|
print(f"Final observable: {final_observable}")
|
64
64
|
print(f"Expectation value: {expectation_value}")
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import pytest
|
2
|
+
from pyrauli import SymbolicObservable, SymbolicCircuit
|
3
|
+
|
4
|
+
def rzz(qc, q1, q2, v):
|
5
|
+
qc.add_operation("cx", q1, q2)
|
6
|
+
qc.add_operation("rz", q2, v)
|
7
|
+
qc.add_operation("cx", q1, q2)
|
8
|
+
|
9
|
+
def rx(qc, q, v):
|
10
|
+
qc.add_operation("h", q)
|
11
|
+
qc.add_operation("rz", q, v)
|
12
|
+
qc.add_operation("h", q)
|
13
|
+
|
14
|
+
@pytest.fixture(scope="module")
|
15
|
+
def maxcut_qaoa_N4P1():
|
16
|
+
"""Fixture"""
|
17
|
+
obs = SymbolicObservable(["ZZII", "ZIZI", "IZZI", "ZIIZ", "IZIZ", "IIZZ"])
|
18
|
+
qc = SymbolicCircuit(4)
|
19
|
+
|
20
|
+
for i in range(4):
|
21
|
+
qc.add_operation("h", i)
|
22
|
+
|
23
|
+
rzz(qc, 0, 1, "tz")
|
24
|
+
rzz(qc, 0, 3, "tz")
|
25
|
+
rzz(qc, 0, 2, "tz")
|
26
|
+
|
27
|
+
rx(qc, 0, "tx")
|
28
|
+
|
29
|
+
rzz(qc, 1, 2, "tz")
|
30
|
+
rzz(qc, 1, 3, "tz")
|
31
|
+
|
32
|
+
rx(qc, 1, "tx")
|
33
|
+
rzz(qc, 2, 3, "tz")
|
34
|
+
|
35
|
+
rx(qc, 2, "tx")
|
36
|
+
rx(qc, 3, "tx")
|
37
|
+
|
38
|
+
return qc, obs
|
39
|
+
|
40
|
+
def test_qaoa_N4P1_run(maxcut_qaoa_N4P1, benchmark):
|
41
|
+
qc, obs = maxcut_qaoa_N4P1
|
42
|
+
|
43
|
+
benchmark(qc.run, obs)
|
@@ -60,6 +60,20 @@ the hood.
|
|
60
60
|
:end-before: # [estimator_complex]
|
61
61
|
:dedent: 4
|
62
62
|
|
63
|
+
Full PUBs support
|
64
|
+
-----------------
|
65
|
+
|
66
|
+
``pyrauli`` intends to be fully compatible with Qiskit and the provided backends and estimators should be able to be swapped with any other qiskit backend. Therefore, :py:class:`~pyrauli.PyrauliEstimator` and :py:class:`~pyrauli.PBackend` `.run` methods allows for any PUB (see `Qiskit documentation on PUBs https://qiskit.qotlabs.org/docs/guides/primitive-input-output`).
|
67
|
+
|
68
|
+
Below is an example of what is possible:
|
69
|
+
|
70
|
+
.. literalinclude:: /../tests/test_backend.py
|
71
|
+
:language: python
|
72
|
+
:start-after: # [qiskit_multiparameters]
|
73
|
+
:end-before: # [qiskit_multiparameters]
|
74
|
+
:dedent: 4
|
75
|
+
|
76
|
+
|
63
77
|
Qiskit and reverse qubit ordering
|
64
78
|
---------------------------------
|
65
79
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
.. _how_to_symbolic:
|
2
|
+
|
3
|
+
Symbolic Simulation
|
4
|
+
===================
|
5
|
+
|
6
|
+
`pyrauli` offers a powerful symbolic simulation mode that allows you to work with parameterized quantum circuits. Instead of providing fixed numerical values for parameters like gate angles or noise strengths, you can use symbolic variables. This is particularly useful for tasks like variational algorithms, gradient calculations, and sensitivity analysis, where you need to explore a function's behavior over a range of parameter values.
|
7
|
+
|
8
|
+
The Symbolic Toolkit
|
9
|
+
--------------------
|
10
|
+
|
11
|
+
The symbolic mode is built on three core classes:
|
12
|
+
|
13
|
+
* :py:class:`~pyrauli.SymbolicCoefficient`: Represents a mathematical expression that can include variables, constants, and standard mathematical operations.
|
14
|
+
* :py:class:`~pyrauli.SymbolicObservable`: An observable whose terms have `SymbolicCoefficient` objects as coefficients.
|
15
|
+
* :py:class:`~pyrauli.SymbolicCircuit`: A circuit that can accept `SymbolicCoefficient` objects as parameters for its operations.
|
16
|
+
|
17
|
+
Working with `SymbolicCoefficient`
|
18
|
+
------------------------------------
|
19
|
+
|
20
|
+
The :py:class:`~pyrauli.SymbolicCoefficient` is the fundamental building block. You can create one from a number or a string (which becomes a variable name).
|
21
|
+
|
22
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_coefficient_guide.py
|
23
|
+
:language: python
|
24
|
+
:start-after: # [symbolic_init]
|
25
|
+
:end-before: # [symbolic_init]
|
26
|
+
:dedent: 4
|
27
|
+
|
28
|
+
These objects support standard mathematical operations, allowing you to build complex expressions.
|
29
|
+
|
30
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_coefficient_guide.py
|
31
|
+
:language: python
|
32
|
+
:start-after: # [symbolic_ops]
|
33
|
+
:end-before: # [symbolic_ops]
|
34
|
+
:dedent: 4
|
35
|
+
|
36
|
+
Evaluating Expressions
|
37
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
38
|
+
|
39
|
+
A key feature is the ability to substitute variables with values. There are two ways to do this:
|
40
|
+
|
41
|
+
1. **.evaluate()**: This method substitutes all variables and computes a final floating-point number. It will raise an error if any variables are left unbound.
|
42
|
+
2. **.symbolic_evaluate()**: This method substitutes only the specified variables, returning a new, potentially simpler `SymbolicCoefficient`.
|
43
|
+
|
44
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_coefficient_guide.py
|
45
|
+
:language: python
|
46
|
+
:start-after: # [symbolic_evaluate]
|
47
|
+
:end-before: # [symbolic_evaluate]
|
48
|
+
:dedent: 4
|
49
|
+
|
50
|
+
Simplifying Expressions
|
51
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
52
|
+
|
53
|
+
The :py:meth:`~pyrauli.SymbolicCoefficient.simplified` method applies arithmetic rules (like `x*1=x` or `x+0=x`) to reduce the complexity of an expression.
|
54
|
+
|
55
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_coefficient_guide.py
|
56
|
+
:language: python
|
57
|
+
:start-after: # [symbolic_simplify]
|
58
|
+
:end-before: # [symbolic_simplify]
|
59
|
+
:dedent: 4
|
60
|
+
|
61
|
+
Constructing a `SymbolicObservable`
|
62
|
+
-------------------------------------
|
63
|
+
|
64
|
+
A :py:class:`~pyrauli.SymbolicObservable` works just like a regular :py:class:`~pyrauli.Observable`, but its coefficients are symbolic.
|
65
|
+
|
66
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_observable_guide.py
|
67
|
+
:language: python
|
68
|
+
:start-after: # [symbolic_obs_init]
|
69
|
+
:end-before: # [symbolic_obs_init]
|
70
|
+
:dedent: 4
|
71
|
+
|
72
|
+
The :py:meth:`~pyrauli.SymbolicObservable.simplify` method on an observable will simplify the symbolic coefficients of all its terms. You can also pass a dictionary of variable substitutions to this method.
|
73
|
+
|
74
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_observable_guide.py
|
75
|
+
:language: python
|
76
|
+
:start-after: # [symbolic_obs_simplify]
|
77
|
+
:end-before: # [symbolic_obs_simplify]
|
78
|
+
:dedent: 4
|
79
|
+
|
80
|
+
Building and Running a `SymbolicCircuit`
|
81
|
+
------------------------------------------
|
82
|
+
|
83
|
+
The end-to-end workflow is straightforward. You build a :py:class:`~pyrauli.SymbolicCircuit` using variable names for your parameters and then run it on a :py:class:`~pyrauli.SymbolicObservable`. The simulation propagates the symbolic expressions through the circuit according to the rules of quantum mechanics.
|
84
|
+
|
85
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_circuit_snippet.py
|
86
|
+
:language: python
|
87
|
+
:start-after: # [symbolic_circuit]
|
88
|
+
:end-before: # [symbolic_circuit]
|
89
|
+
:dedent: 4
|
90
|
+
|
91
|
+
The final result is a new :py:class:`~pyrauli.SymbolicObservable`. To get the final expectation value, you call its :py:meth:`~pyrauli.SymbolicObservable.expectation_value` method, which returns a `SymbolicCoefficient`. You can then evaluate this coefficient for any set of concrete parameter values.
|
92
|
+
|
93
|
+
.. literalinclude:: /../tests/snippets/test_symbolic_circuit_snippet.py
|
94
|
+
:language: python
|
95
|
+
:start-after: # [symbolic_evaluation]
|
96
|
+
:end-before: # [symbolic_evaluation]
|
97
|
+
:dedent: 4
|
98
|
+
|
99
|
+
This workflow allows you to run the simulation once to get a general symbolic result and then analyze that result for many different parameter values without needing to re-run the simulation each time.
|
@@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build"
|
|
5
5
|
|
6
6
|
[project]
|
7
7
|
name = "pyrauli"
|
8
|
-
version = "0.
|
8
|
+
version = "0.4.0"
|
9
9
|
description = "A very fast and easy to use Quantum circuit simulator relying on Pauli propagation. Compatible with qiskit."
|
10
10
|
readme = "README.md"
|
11
11
|
requires-python = ">=3.9"
|
@@ -25,13 +25,18 @@ Homepage = "https://github.com/zefresk/pyrauli"
|
|
25
25
|
|
26
26
|
[project.optional-dependencies]
|
27
27
|
qiskit = [
|
28
|
-
"qiskit>=2.0.0"
|
28
|
+
"qiskit>=2.0.0",
|
29
|
+
"numpy"
|
30
|
+
]
|
31
|
+
symbolic = [
|
32
|
+
"sympy"
|
29
33
|
]
|
30
34
|
test = [
|
31
35
|
"pytest>=8.0.0",
|
32
36
|
"pytest-cov>=5.0.0",
|
33
37
|
"pytest-benchmark",
|
34
38
|
"numpy",
|
39
|
+
"sympy"
|
35
40
|
]
|
36
41
|
|
37
42
|
[tool.scikit-build.wheel]
|
@@ -12,6 +12,7 @@ from ._core import (
|
|
12
12
|
SchedulingPolicy, NeverPolicy, AlwaysBeforeSplittingPolicy,
|
13
13
|
AlwaysAfterSplittingPolicy, Circuit, OperationType, Timing,
|
14
14
|
SimulationState, CompressionResult, LambdaPolicy,
|
15
|
+
SymbolicCoefficient, SymbolicPauliTerm, SymbolicObservable, SymbolicNoise, SymbolicNoiseModel, SymbolicTruncator, SymbolicWeightTruncator, SymbolicNeverTruncator, SymbolicMultiTruncator, SymbolicCircuit
|
15
16
|
)
|
16
17
|
|
17
18
|
__all__ = [
|
@@ -22,8 +23,24 @@ __all__ = [
|
|
22
23
|
"AlwaysBeforeSplittingPolicy", "AlwaysAfterSplittingPolicy", "Circuit",
|
23
24
|
"OperationType", "Timing", "SimulationState", "CompressionResult",
|
24
25
|
"LambdaPolicy",
|
26
|
+
"SymbolicCoefficient", "SymbolicObservable", "SymbolicPauliTerm", "SymbolicNoise", "SymbolicNoiseModel", "SymbolicTruncator", "SymbolicWeightTruncator", "SymbolicNeverTruncator", "SymbolicMultiTruncator", "SymbolicCircuit"
|
27
|
+
|
25
28
|
]
|
26
29
|
|
30
|
+
try:
|
31
|
+
import sympy
|
32
|
+
def to_sympy(self):
|
33
|
+
"""
|
34
|
+
Converts the SymbolicCoefficient to a SymPy expression.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
A SymPy expression equivalent to the SymbolicCoefficient.
|
38
|
+
"""
|
39
|
+
return sympy.sympify(self.to_string())
|
40
|
+
SymbolicCoefficient.to_sympy = to_sympy
|
41
|
+
except ImportError:
|
42
|
+
pass
|
43
|
+
|
27
44
|
# Conditionally import Qiskit-related functionality
|
28
45
|
try:
|
29
46
|
from .backend import PBackend
|