Skip to content

Commit

Permalink
Merge pull request #8 from qognitive/feature/restructure-project-for-…
Browse files Browse the repository at this point in the history
…python

Feature/restructure project for python
  • Loading branch information
stand-by authored Jul 22, 2024
2 parents 3e4ff6c + b328a92 commit 782ef0c
Show file tree
Hide file tree
Showing 34 changed files with 323 additions and 48 deletions.
1 change: 1 addition & 0 deletions .github/compiler_setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# noqa: D100
import os
import sys

Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/all_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ jobs:
# Build your program with the given configuration
run: |
cmake --build ${{github.workspace}}/build --verbose --parallel
du build/tests/test_pauli_op
- name: Test C++
env:
OMP_NUM_THREADS: 2
CPP_TEST_DIR: build/fast_pauli/cpp/tests
run: |
# ctest --test-dir build --verbose # TODO not using bc of PauliOp problems on CI
./build/tests/test_factory
./build/tests/test_pauli
./build/tests/test_pauli_op --test-case-exclude="*multistring*"
./build/tests/test_pauli_string
./build/tests/test_summed_pauli_op
./${CPP_TEST_DIR}/test_factory
./${CPP_TEST_DIR}/test_pauli
./${CPP_TEST_DIR}/test_pauli_op --test-case-exclude="*multistring*"
./${CPP_TEST_DIR}/test_pauli_string
./${CPP_TEST_DIR}/test_summed_pauli_op
# - name: Test Python
# run: PYTHONPATH=build:$PYTHONPATH pytest -v test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ __pycache__
*.xwm

# Misc
.venv
.vscode
.coverage
*.zip
*_package
venv
logs
scratch
notes
Expand Down
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ repos:
rev: v0.10.0
hooks:
- id: yamlfmt
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: trailing-whitespace
# C/C++
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v18.1.8
Expand All @@ -20,5 +24,22 @@ repos:
hooks:
# Run the linter.
- id: ruff
types_or: [python, pyi, jupyter]
args: ["--config", "pyproject.toml", "--fix", "--show-fixes"]
# Run the formatter.
- id: ruff-format
types_or: [python, pyi, jupyter]
args: ["--config", "pyproject.toml"]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
args: ["--config", "pyproject.toml"]
# Misc
- repo: https://github.com/codespell-project/codespell
rev: v2.2.4
hooks:
- id: codespell
args: ["--ignore-words-list", "bra,ket"]
additional_dependencies:
- tomli # So we can configure it to use pyproject.toml
9 changes: 5 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ find_package(OpenMP REQUIRED)

# Our primary target
add_library(fast_pauli INTERFACE)
target_include_directories(fast_pauli
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
target_include_directories(
fast_pauli INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/fast_pauli/cpp/include/)
target_link_libraries(fast_pauli INTERFACE fmt::fmt mdspan OpenMP::OpenMP_CXX)
target_compile_options(
fast_pauli
Expand All @@ -66,7 +66,8 @@ target_compile_options(
# Testing
include(CTest)
enable_testing()
add_subdirectory(tests)
# TODO use proper variable for project root
add_subdirectory(fast_pauli/cpp/tests)

# Examples
add_subdirectory(examples)
add_subdirectory(fast_pauli/cpp/examples)
35 changes: 35 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.PHONY: build
build:
cmake -B build
cmake --build build --parallel
# TODO in general python build should internally trigger cmake, but for now
# let's keep cmake lines here as we don't have any python build process yet
python -m pip cache purge
python -m pip install --upgrade pip
python -m pip install ".[dev]"
python -m build .

.PHONY: tests
tests:
ctest --test-dir build
python -m pytest fast_pauli/py/tests
python -m pytest tests

.PHONY: clean
clean:
rm -rf build dist

lint:
pre-commit run --all-files -- ruff
pre-commit run --all-files -- mypy
pre-commit run --all-files -- codespell

# run with make -i to ignore errors and run all three formatters
format:
pre-commit run --all-files -- ruff-format
pre-commit run --all-files -- yamlfmt
pre-commit run --all-files -- trailing-whitespace

.PHONY: pre-commit-setup
pre-commit-setup:
pre-commit install
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- [X] PauliString
- [ ] PauliOp
- [ ] SummedPauliOp
- [ ] Figure out the tranpose non-sense or support both (some functions take the transpose of the states and others don't)
- [ ] Figure out the transpose non-sense or support both (some functions take the transpose of the states and others don't)
- [ ] Clean up `apply_batch` we shouldn't need to pass a coeff
- [ ] Clean up tests
- [ ] Add apply method to SummedPauliOp that takes precomputed weighted data
Expand Down
1 change: 1 addition & 0 deletions benchmarks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Benchmarking module to compare C++ implementation against numpy."""
17 changes: 0 additions & 17 deletions benchmarks/pauli_helpers.py

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ template <std::floating_point T> struct PauliOp {
ps.apply_batch(new_states_local, states, c);
}

// Do the reduction and tranpose back
// Do the reduction and transpose back
#pragma omp for schedule(static) collapse(2)
for (size_t i = 0; i < new_states.extent(0); ++i) {
for (size_t t = 0; t < new_states.extent(1); ++t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ struct PauliString {
* columns
*
* @tparam T The floating point base to use for all the complex numbers
* @param new_states_T The outpus states after applying the PauliString
* @param new_states_T The output states after applying the PauliString
* (n_data x n_dim)
* @param states_T THe original states to apply the PauliString to (n_data x
* n_dim)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ TEST_CASE("test apply batch") {
size_t const n_states = 10;

// Testing each of these pauli strings individually
// NOTE: apply_batch takes the tranpose of the states and new_states
// NOTE: apply_batch takes the transpose of the states and new_states
for (PauliString ps : {"IXYZ", "YYIX", "XXYIYZ", "IZIXYYZ"}) {
size_t const dims = ps.dims();

Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions fast_pauli/py/pypauli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""pypauli module provides implementations to benchmark and test c++ library."""
43 changes: 43 additions & 0 deletions fast_pauli/py/pypauli/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Separate file with helper functions for Pauli matrices."""

import numpy as np


def pauli_matrices() -> dict[str | int, np.ndarray]:
"""Provide a dictionary containing the Pauli matrices.
The Pauli matrices are a set of 2x2 matrices commonly used in quantum mechanics.
This function returns a dictionary with keys representing the matrix names
("I", "X", "Y", "Z", 0, 1, 2, 3) and values representing the corresponding matrix.
Returns
-------
dict: A dictionary containing the Pauli matrices.
"""
s0 = np.array([[1, 0], [0, 1]], dtype=np.complex128)
s1 = np.array([[0, 1], [1, 0]], dtype=np.complex128)
s2 = np.array([[0, -1j], [1j, 0]], dtype=np.complex128)
s3 = np.array([[1, 0], [0, -1]], dtype=np.complex128)
return {"I": s0, "X": s1, "Y": s2, "Z": s3, 0: s0, 1: s1, 2: s2, 3: s3}


def naive_pauli_converter(string: str) -> np.ndarray:
"""Convert Pauli string representation into corresponding dense matrix.
Parameters
----------
string : str
The string representation of Pauli string.
Returns
-------
np.ndarray
The matrix representation of the Pauli string.
"""
paulis = pauli_matrices()
matrix = paulis[string[-1]]
for p in reversed(string[:-1]):
matrix = np.kron(paulis[p], matrix)
return matrix
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
"""Efficient operations on Pauli string using numpy."""

import numpy as np


class PauliString:
"""Class representing a Pauli string with efficient operations."""

def __init__(self, string: str) -> None:
"""Initialize the PauliString object.
Args:
----
string: The input string representing the Pauli string.
Must contain only I, X, Y, Z characters.
"""
if not all([c in "IXYZ" for c in string]):
raise ValueError(f"Invalid pauli string {string}")

Expand All @@ -11,13 +23,25 @@ def __init__(self, string: str) -> None:
self.weight = len(string) - string.count("I")

def dense(self) -> np.ndarray:
"""Return the dense matrix representation of the Pauli string."""
columns, values = compose_sparse_pauli(self.string)

matrix = np.zeros((columns.size, values.size), dtype=np.complex128)
matrix[np.arange(columns.size), columns] = values
return matrix

def multiply(self, state: np.ndarray) -> np.ndarray:
"""Efficient multiplication of Pauli string with a given state.
Args:
----
state: The input state as a numpy array.
Returns
-------
The result of multiplying the Pauli string with the state.
"""
if state.shape[0] != self.dim or state.ndim > 2:
raise ValueError(f"Provided state has inconsistent shape {state.shape}")

Expand All @@ -30,6 +54,18 @@ def multiply(self, state: np.ndarray) -> np.ndarray:


def compose_sparse_pauli(string: str) -> tuple[np.ndarray, np.ndarray]:
"""Produce sparse representation of the pauli string.
Args:
----
string: The input string representing the Pauli string.
Must contain only I, X, Y, Z characters.
Returns
-------
A tuple containing the column numbers and values of the sparse matrix.
"""
n_qubits = len(string)
n_vals = 1 << n_qubits
n_ys = string.count("Y")
Expand Down Expand Up @@ -77,7 +113,18 @@ def compose_sparse_pauli(string: str) -> tuple[np.ndarray, np.ndarray]:
return cols, vals


def compose_sparse_diag_pauli(string) -> np.ndarray:
def compose_sparse_diag_pauli(string: str) -> np.ndarray:
"""Produce sparse representation of diagonal pauli string.
Args:
----
string: A Pauli string containing only 'I' and 'Z' characters.
Returns
-------
np.ndarray: diagonal values from resulting sparse matrix.
"""
if "X" in string or "Y" in string:
raise ValueError("Pauli string must contain only I and Z characters")

Expand Down
1 change: 1 addition & 0 deletions fast_pauli/py/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Unit tests for pypauli module."""
Loading

0 comments on commit 782ef0c

Please sign in to comment.