Skip to content

Commit

Permalink
Merge branch 'main' of github.com:qognitive/fast-pauli into docs/maco…
Browse files Browse the repository at this point in the history
…s_install
  • Loading branch information
jamesETsmith committed Oct 10, 2024
2 parents 6fbae0a + 2da163e commit 61fcec7
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 40 deletions.
3 changes: 1 addition & 2 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runs:
run: python3 ./.github/compiler_setup.py ${CXX} ${BUILD_OS}
- name: Install Python dependencies
shell: bash
run: python -m pip install cmake scikit-build-core
run: python -m pip install cmake scikit-build-core setuptools_scm build
- name: Build
shell: bash
run: |
Expand All @@ -24,5 +24,4 @@ runs:
- name: Build Publishable release distributions for PyPI
shell: bash
run: |
python -m pip install build
python -m build -s
22 changes: 20 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
permissions:
id-token: write # This is required for requesting the JWT
jobs:
main-build:
source_dist_build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand All @@ -28,5 +28,23 @@ jobs:
env:
BUILD_OS: ${{ matrix.os }}
CXX: ${{ matrix.compiler }}
- name: Publish release distributions to PyPI
- name: Publish source dist release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
wheels_build:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
# macos-13 is an intel runner, macos-14 is apple silicon
# os: [ubuntu-latest, macos-13, macos-14]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v4
- name: Build wheels
uses: pypa/[email protected]
with:
config-file: "{package}/pyproject.toml"
- name: Publish wheel release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: wheelhouse
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ find_package(OpenMP REQUIRED)

# Setup Python
find_package(Python 3.10 # This is a minimum version
COMPONENTS Interpreter Development REQUIRED)
COMPONENTS Interpreter Development.Module REQUIRED)

# Our primary target
add_library(fast_pauli INTERFACE)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ format:

.PHONY: clean
clean:
rm -rf build dist
rm -rf build dist wheelhouse

.PHONY: pre-commit-setup
pre-commit-setup:
Expand Down
105 changes: 73 additions & 32 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,33 @@
Getting Started
=====================

Welcome to Fast-Pauli from `Qognitive <https://www.qognitive.io/>`_, an open-source Python / C++ library for optimized operations on Pauli matrices and Pauli strings
based on `PauliComposer <https://arxiv.org/abs/2301.00560>`_. In this guide,
we'll introduce some of the important operations to help users get started. For more details,
see the API documentation.
Welcome to :code:`fast-pauli` from `Qognitive <https://www.qognitive.io/>`_, an open-source Python / C++ library for optimized operations on Pauli matrices and Pauli strings
based on `PauliComposer <https://arxiv.org/abs/2301.00560>`_.
In this guide, we'll introduce some of the important operations to help users get started as well as some conceptual background on Pauli matrices and Pauli strings.

For tips on installing the library, check out the guide: :doc:`index`.
If you want to follow along with this guide, please follow the installation instructions in :doc:`index`.
For more details on our programmatic interface, see the :doc:`python_api` or :doc:`cpp_api` documentation.

Pauli Matrices
------------------------

For a conceptual overview of Pauli matrices, see `here <https://en.wikipedia.org/wiki/Pauli_matrices>`_.
For a more in-depth overview of Pauli matrices, see `here <https://en.wikipedia.org/wiki/Pauli_matrices>`_.

In math and physics, a `Pauli matrix <https://en.wikipedia.org/wiki/Pauli_matrices>`_, named after the physicist Wolfgang Pauli, is any one of the special 2 x 2 complex matrices in the set (often denoted by the greek letter :math:`\sigma`) :

.. math::
\sigma_x = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}
\sigma_y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}
\sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}
All the Pauli matrices share the properties that they are:

1. Hermitian (equal to their own conjugate transpose) :math:`\sigma_i = \sigma_i^\dagger` for all :math:`i \in \{x, y, z\}`
2. Involutory (they are their own inverse) :math:`\sigma_i^2 = \sigma_0` for all :math:`i \in \{x, y, z\}`
3. Unitary (their inverse is equal to their conjugate transpose) :math:`\sigma_i^{-1} = \sigma_i^\dagger` for all :math:`i \in \{x, y, z\}`

with the identity matrix :math:`\sigma_0` or the :math:`2 \times 2` Identity matrix :math:`I` being the trivial case.

In ``fast_pauli``, we represent pauli matrices using the ``Pauli`` class. For example, to represent the Pauli matrices, we can do:

Expand All @@ -26,13 +42,17 @@ In ``fast_pauli``, we represent pauli matrices using the ``Pauli`` class. For ex
pauli_y = fp.Pauli('Y')
pauli_z = fp.Pauli('Z')
We can also multiply two ``Pauli`` objects together to get a ``Pauli`` object representing the tensor product of the two pauli matrices.
str(pauli_0) # returns "I"
We can also multiply two ``Pauli`` objects together to get a ``Pauli`` object representing the matrix product of the two pauli matrices.
The result includes a phase factor because the product of two Pauli matrices is not another Pauli matrix, but rather a Pauli matrix multiplied by either ``i`` or ``-i`` which we call a phase factor.

For example, we can compute the resulting ``Pauli`` object from multiplying :math:`\sigma_x` and :math:`\sigma_y` as follows:

.. code-block:: python
# phase = i, new_pauli = fp.Pauli('Z')
phase, new_pauli = pauli_x @ pauli_y
# returns "I"
str(pauli_0)
From here, we can also convert our ``Pauli`` object back to a dense numpy array if we'd like:

Expand All @@ -43,7 +63,23 @@ From here, we can also convert our ``Pauli`` object back to a dense numpy array
Pauli Strings
------------------------

In ``fast_pauli``, we represent Pauli strings using the ``PauliString`` class. For example, to construct the Pauli string ``X, Y, Z``, we can do:
Pauli strings are tensor-product combinations of Pauli matrices. For example, the following is a valid Pauli string:

.. math::
\mathcal{\hat{P}} = \sigma_x \otimes \sigma_y \otimes \sigma_z
where :math:`\otimes` denotes the tensor or `Kronecker <https://en.wikipedia.org/wiki/Kronecker_product>`_ product, and we can more simply denote by

.. math::
\mathcal{\hat{P}} = XYZ
A Pauli string of length ``N`` is a tensor product of ``N``
Pauli matrices. We can represent a ``N``-length Pauli String as a :math:`2^N \times 2^N` operator or matrix (in physics, an operator is represented by a matrix in a given basis), so ``XYZ`` is a :math:`8 \times 8` matrix.
Since these operators can get large very quickly, ``fast_pauli`` represents Pauli strings sparsely as an array of ``Pauli`` objects.

For example, to construct the Pauli string ``XYZ``, we can do:

.. code-block:: python
Expand All @@ -61,19 +97,17 @@ Pauli Strings also support operations like addition, multiplication, and more. F
P1.dim
P1.n_qubits
# Add two Pauli strings. Return type is a PauliOp because
# the product is not a Pauli string
P3 = P1 + P2
# Multiply two Pauli strings.
phase, new_string = P1 @ P2
We can also do more complicated things, like compute the action of a Pauli string :math:`\mathcal{\hat{P}}` on a quantum state :math:`| \psi \rangle`, :math:`\mathcal{\hat{P}}| \psi \rangle`, or
compute the expectation value of a Pauli string with a state :math:`\langle \psi | \mathcal{\hat{P}} | \psi \rangle`:
We can also do more complicated things, like compute the action of a Pauli string :math:`\mathcal{\hat{P}}` on a vector :math:`| \psi \rangle`, :math:`\mathcal{\hat{P}}| \psi \rangle`, or
compute the expectation value of a Pauli string with a state :math:`\langle \psi | \mathcal{\hat{P}} | \psi \rangle`. As a side note, in this guide we will use state and vector interchangeably:

.. code-block:: python
import numpy as np
# Apply P to a state
P = fp.PauliString('XY')
state = np.array([1, 0, 0, 1], dtype=complex)
Expand All @@ -82,7 +116,7 @@ compute the expectation value of a Pauli string with a state :math:`\langle \psi
# Compute the expected value of P with respect to a state or a batch of states
value = P.expectation_value(state)
states = np.random.randn(8, 8) + 1j * np.random.randn(8, 8)
states = np.random.randn(4, 8) + 1j * np.random.randn(4, 8)
values = P.expectation_value(states)
We can also convert ``PauliString`` objects back to dense numpy arrays if we'd like, or extract their string representation:
Expand All @@ -91,16 +125,16 @@ We can also convert ``PauliString`` objects back to dense numpy arrays if we'd l
P = fp.PauliString('XYZ')
P_np = P.to_tensor()
# Returns "XYZ"
P_str = str(P)
For more details on the ``PauliString`` class, see the Python or C++ API documentation.
P_str = str(P) # Returns "XYZ"
For more details on the ``PauliString`` class, see the :doc:`python_api` or :doc:`cpp_api` documentation.

Pauli Operators
------------------------

The ``PauliOp`` class lets us represent operators that are linear combinations of Pauli strings with complex coefficients. More specifically,
we can represent an arbitrary operator :math:`A` as a sum of Pauli strings :math:`P_i` with complex coefficients :math:`c_i`:
The ``PauliOp`` class lets us represent operators that are linear combinations of Pauli strings with complex coefficients.
For example, we can represent any arbitrary operator :math:`A` as a sum of Pauli strings :math:`P_i` with complex coefficients :math:`c_i`:

.. math::
Expand All @@ -111,8 +145,8 @@ that represents the operator :math:`A = 0.5 * XYZ + 0.5 * YYZ`, we can do:

.. code-block:: python
coeffs = np.array([0.5, 0.5], dtype=complex)
pauli_strings = ['XYZ', 'YYZ']
coeffs = np.array([0.5, 0.5], dtype=complex) # represent c_i in the sum above
pauli_strings = ['XYZ', 'YYZ'] # represent P_i in the sum above
A = fp.PauliOp(coeffs, pauli_strings)
# Get the number of qubits the operator acts on,
Expand All @@ -122,8 +156,8 @@ that represents the operator :math:`A = 0.5 * XYZ + 0.5 * YYZ`, we can do:
A.dim
A.n_pauli_strings
Just like with ``PauliString`` objects, we can apply ``PauliOp`` objects to a set of quantum states or compute expectation values, as well as arithmetic
operations and dense matrix conversions. Just like with ``PauliString`` objects, we can also convert ``PauliOp`` objects back to dense numpy arrays if we'd like
Just like with ``PauliString`` objects, we can apply ``PauliOp`` objects to a set of vectors, or compute expectation values, as well as arithmetic
operations. Just like with ``PauliString`` objects, we can also convert ``PauliOp`` objects back to dense numpy arrays if we'd like
or get their string representation, in this case a list of strings:

.. code-block:: python
Expand All @@ -132,6 +166,13 @@ or get their string representation, in this case a list of strings:
pauli_strings = ['XYZ', 'YYZ']
A = fp.PauliOp(coeffs, pauli_strings)
# Adding two Pauli strings returns a PauliOp.
# The returned object is a PauliOp because
# the sum is a linear combination of Pauli strings
P1 = fp.PauliString('XYZ')
P2 = fp.PauliString('YZX')
O = P1 + P2
# PauliOp supports addition, subtraction, multiplication,
# scaling, as well as have PauliString objects
# as the second operand. All valid operations:
Expand All @@ -141,8 +182,8 @@ or get their string representation, in this case a list of strings:
s = fp.PauliString('XYZ')
A4 = A1 + s
# Apply A to a state or set of states
states = np.random.rand(10, 8) + 1j * np.random.rand(10, 8)
# Apply A to a single state / vector or set
states = np.random.rand(8, 10) + 1j * np.random.rand(8, 10)
new_states = A.apply(states)
# Compute the expectation value of A with respect to a state
Expand All @@ -156,19 +197,19 @@ or get their string representation, in this case a list of strings:
Qiskit Integration
------------------------
``Fast-Pauli`` also has integration with `IBM's Qiskit SDK <https://www.ibm.com/quantum/qiskit>`_, allowing for easy interfacing with certain Qiskit objects. For example, we can convert
``fast_pauli`` also has integration with `IBM's Qiskit SDK <https://www.ibm.com/quantum/qiskit>`_, allowing for easy interfacing with certain Qiskit objects. For example, we can convert
between ``PauliOp`` objects and ``SparsePauliOp`` objects from Qiskit:

.. code-block:: python
# Convert a Fast-Pauli PauliOp to a Qiskit SparsePauliOp object and back
# Convert a fast_pauli PauliOp to a Qiskit SparsePauliOp object and back
O = fp.PauliOp([1], ['XYZ'])
qiskit_op = fp.to_qiskit(O)
fast_pauli_op = fp.from_qiskit(qiskit_op)
# Convert a Fast-Pauli PauliString to a Qiskit Pauli object
# Convert a fast_pauli PauliString to a Qiskit Pauli object
P = fp.PauliString('XYZ')
qiskit_pauli = fp.to_qiskit(P)
For more details on Qiskit conversions, see the Python or C++ API documentation.
For more details on Qiskit conversions, see the :doc:`python_api` documentation.

19 changes: 17 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
requires = [
# scikit-build-core uses cmake as needed (do not list it here)
"scikit-build-core",
"setuptools_scm[toml]>=8",
]
build-backend = "scikit_build_core.build"

Expand All @@ -41,24 +42,27 @@ authors = [
{ name="Jeffrey Berger", email="[email protected]" },
]
dependencies = ["numpy", "qiskit", "qiskit-algorithms"]
version = "0.0.8"
requires-python = ">= 3.10"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dynamic = ["version"]


[project.optional-dependencies]
dev = [
"cibuildwheel",
"clang-format",
"cmake-format",
"pre-commit",
"mypy",
"ruff",
"pytest",
"pytest-benchmark",
"setuptools_scm",
"sphinx",
"sphinx_rtd_theme",
"sphinx-autobuild",
Expand Down Expand Up @@ -178,5 +182,16 @@ docstring-code-format = true
# pip install -e . --verbose -C cmake.args="-DCMAKE_CXX_COMPILER=clang++-18"
[tool.scikit-build]
cmake.build-type = "Release"
build-dir = "build"
build-dir = "build/{wheel_tag}"
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
sdist.include = ["fast_pauli/__version__.py"]
# TODO add more options here

[tool.setuptools_scm]
write_to = "fast_pauli/__version__.py"

[tool.cibuildwheel]
build = ["cp310-manylinux_x86_64", "cp311-manylinux_x86_64", "cp312-manylinux_x86_64", "cp313-manylinux_x86_64"]
build-frontend = "build"
test-requires = "pytest"
test-command = "pytest -s -vv --import-mode importlib {project}/tests/fast_pauli"

0 comments on commit 61fcec7

Please sign in to comment.