Skip to content

Commit

Permalink
Merge pull request #2 from qognitive/feature/initial_setup
Browse files Browse the repository at this point in the history
Initial Setup
  • Loading branch information
jamesETsmith authored Jun 6, 2024
2 parents 20aa677 + 6b8cf05 commit 08f0c24
Show file tree
Hide file tree
Showing 23 changed files with 2,627 additions and 1 deletion.
22 changes: 22 additions & 0 deletions .github/compiler_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import sys

if __name__ == "__main__":

# TODO only for ubuntu

compiler = sys.argv[1]
os.system("sudo apt-get update")

if "g++" in compiler and "clang" not in compiler:
version = compiler.split("-")[1]
os.system(f"sudo apt-get -y install gcc-{version} g++-{version}")
elif "clang" in compiler:
version = compiler.split("-")[1]
print(f"Installing clang {compiler}")
os.system("wget https://apt.llvm.org/llvm.sh")
os.system("chmod +x llvm.sh")
os.system(f"sudo ./llvm.sh {version}")
os.system(f"sudo apt-get install -y libomp-{version}-dev")
else:
raise ValueError(f"Compiler {compiler} not supported")
46 changes: 46 additions & 0 deletions .github/workflows/all_push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: All push

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
compiler: [g++-12, clang++-17]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v2
with:
submodules: recursive

- name: Setup compiler
run: python3 ./.github/compiler_setup.py ${{ matrix.compiler }}

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install Python dependencies
run: python -m pip install numpy pytest cmake

- name: Configure CMake
env:
CXX: ${{ matrix.compiler }}
run: cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_BUILD_TYPE=Release

- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build --parallel

- name: Test C++
run: ctest --test-dir build --parallel

# - name: Test Python
# run: PYTHONPATH=build:$PYTHONPATH pytest -v test
75 changes: 75 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#
# Boilerplate CMakeLists.txt for C++ projects
#
cmake_minimum_required(VERSION 3.25)

set(CMAKE_EXPORT_COMPILE_COMMANDS
TRUE
CACHE BOOL "Export compile commands to build directory" FORCE)

include(cmake/CPM.cmake)
# TODO adding a package lock to help with SBOM
# cpmusepackagelock(package-lock.cmake)

#
# Project specific configuration
#

# Dependencies
cpmaddpackage("gh:doctest/[email protected]")
# cpmaddpackage("gh:p-ranav/[email protected]")
cpmaddpackage("gh:pybind/[email protected]")
cpmaddpackage("gh:fmtlib/fmt#10.2.1")
cpmaddpackage("gh:kokkos/mdspan#b885a2c60ad42f9e1aaa0d317a38105b950cbed0")

#
# User Options
#

# TODO NOT WORKING YET

# option(ENABLE_COVERAGE "Enable coverage reporting" OFF) if(ENABLE_COVERAGE)
# message(STATUS "[FAST_PAULI] Enabling coverage reporting") message(STATUS
# "[FAST_PAULI]") set(FAST_PAULI_EXTRA_CXX_COMPILE_FLAGS "-coverage")
# set(FAST_PAULI_EXTRA_CXX_LD_FLAGS "-lgcov;--coverage") endif()

#
# Fast Pauli
#

project(fast_pauli LANGUAGES CXX)

# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Set up OpenMP
find_package(OpenMP REQUIRED)

# Our primary target
add_library(fast_pauli INTERFACE)
target_include_directories(fast_pauli
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
target_link_libraries(fast_pauli INTERFACE fmt::fmt mdspan OpenMP::OpenMP_CXX)
target_compile_options(
fast_pauli
INTERFACE -g3
-O3
-march=native
-Wall
-Wextra
-Werror
# -stdlib=libc++
${FAST_PAULI_EXTRA_CXX_COMPILE_FLAGS})
target_link_options(fast_pauli INTERFACE ${FAST_PAULI_EXTRA_CXX_LD_FLAGS}
-fuse-ld=mold)
# target_compile_definitions(fast_pauli INTERFACE)

# Testing
include(CTest)
enable_testing()
add_subdirectory(tests)

# Examples
add_subdirectory(examples)
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
# fast-pauli
# Summary

# TODOs
- [ ] Figure out the tranpose non-sense or support both
- [ ] Clean up `apply_batch` we shouldn't need to pass a coeff
- [ ] Add docstrings
- [X] Pauli
- [X] PauliString
- [ ] PauliOp
- [ ] SummedPauliOp
- [ ] Clean up tests
- [X] Clean up test utils
- [X] Add type aliases and factory functions to utils for fast_pauli
- [X] Seach the names and make sure we don't have any overlap with other projects
- [ ] Build out pauli decomposer
- [X] Remove the weights argument and rename to data
- [X] Add namespace
- [ ] Add apply method to SummedPauliOp that takes precomputed weighted data
- [ ] Writeup for docs
- [ ] Add pybind11 interface and python examples
- [ ] Change functions names over to default to parallel impl and use `_serial` for the serial implementation
- [ ] Migrate `PauliOp` and `SummedPauliOp` to only store mdspans rather than copies of the data itself

## Requirements

- CMake >= 3.20
- C++ compiler with OpenMP and C++20 support (LLVM recommended)
- Tested Compilers GCC@12, LLVM@17, LLVM@18
- Python >= 3.10


## Build and Test

```bash
cmake -B build -DCMAKE_CXX_COMPILER=<your_favorite_c++_compiler>
cmake --build build
ctest --test-dir build
```

## Design Choices

The C++ portion of this library relies heavily on spans and views.
These lightweight accessors are helpful and performant, but can lead to dangling spans or accessing bad memory if used improperly.
Developers should familiarize themselves with these dangers by reviewing [this post](https://hackingcpp.com/cpp/std/span.html).
24 changes: 24 additions & 0 deletions cmake/CPM.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors

set(CPM_DOWNLOAD_VERSION 0.39.0)
set(CPM_HASH_SUM "66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef")

if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()

# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)

file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)

include(${CPM_DOWNLOAD_LOCATION})
28 changes: 28 additions & 0 deletions examples/01_pauli_op.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <algorithm>
#include <random>

#include "fast_pauli.hpp"

using namespace fast_pauli;

int main() {
std::vector<PauliString> pauli_strings(100000, "XYZXYZXYZXYZ");

std::vector<std::complex<double>> coeffs(pauli_strings.size(), 1);
PauliOp<double> pauli_op(coeffs, pauli_strings);

size_t const dims = pauli_strings[0].dims();

// Set up random state
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0, 1.0);
std::vector<std::complex<double>> state(dims, 0);
std::generate(state.begin(), state.end(),
[&]() { return std::complex<double>(dis(gen), dis(gen)); });

// Apply the PauliOp
std::vector<std::complex<double>> res = pauli_op.apply(state);

return 0;
}
38 changes: 38 additions & 0 deletions examples/02_pauli_op_multistate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <algorithm>
#include <experimental/mdspan>
#include <random>

#include "fast_pauli.hpp"

using namespace fast_pauli;

int main() {
using mytype = float;

// Setup PauliOp
std::vector<PauliString> pauli_strings(631, "XYZXYZXYZXYZ");
std::vector<std::complex<mytype>> coeffs(pauli_strings.size(), 1);
PauliOp<mytype> pauli_op(coeffs, pauli_strings);

size_t const dims = pauli_strings[0].dims();

// Set up random states
size_t const n_states = 10000;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0, 1.0);
std::vector<std::complex<mytype>> states_raw(dims * n_states, 0);
std::generate(states_raw.begin(), states_raw.end(),
[&]() { return std::complex<mytype>(dis(gen), dis(gen)); });
std::mdspan<std::complex<mytype>, std::dextents<size_t, 2>> states(
states_raw.data(), n_states, dims);

// Apply the PauliOp
std::vector<std::complex<mytype>> new_states_raw(dims * n_states, 0);
std::mdspan<std::complex<mytype>, std::dextents<size_t, 2>> new_states(
new_states_raw.data(), n_states, dims);

// pauli_op.apply_naive(new_states, states);
pauli_op.apply(new_states, states);
return 0;
}
65 changes: 65 additions & 0 deletions examples/03_summed_pauli_op.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <algorithm>
#include <experimental/mdspan>
#include <random>

#include "__pauli.hpp"
#include "__pauli_string.hpp"
#include "fast_pauli.hpp"

using namespace fast_pauli;

int main() {

//
// User settings
//
size_t const n_operators = 10000;
// size_t const n_paulis_per_operator = 631;
size_t const n_qubits = 14;
size_t const weight = 2;
size_t const n_states = 1000;
// std::string fake_pauli_string = "XYZXYZXYZXYZ";
using fp_type = double;

//
// Setup the summed pauli operator
//
// std::vector<PauliString> pauli_strings(n_paulis_per_operator,
// PauliString(fake_pauli_string));
std::vector<PauliString> pauli_strings =
calculate_pauli_strings_max_weight(n_qubits, weight);

size_t const n_paulis_per_operator = pauli_strings.size();
std::vector<std::complex<fp_type>> coeff_raw(
n_paulis_per_operator * n_operators, 1);
SummedPauliOp<fp_type> summed_op{pauli_strings, coeff_raw};

//
// Setup states
//
size_t const dim = summed_op.n_dimensions();
size_t const n_ops = summed_op.n_operators();

std::vector<std::complex<fp_type>> states_raw(dim * n_states, 1);
std::mdspan<std::complex<fp_type>, std::dextents<size_t, 2>> states(
states_raw.data(), dim, n_states);

auto new_states_raw = std::vector<std::complex<fp_type>>(dim * n_states, 0);
std::mdspan<std::complex<fp_type>, std::dextents<size_t, 2>> new_states(
new_states_raw.data(), dim, n_states);

//
// Init weights (aka data)
//
std::vector<fp_type> weights_raw(n_ops * n_states, 1);
std::mdspan<fp_type, std::dextents<size_t, 2>> weights(weights_raw.data(),
n_ops, n_states);

//
// Apply the states
//
// summed_op.apply(new_states, states, weights);
summed_op.apply_parallel(new_states, states, weights);

return 0;
}
10 changes: 10 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Grab all *.cpp files in the directory
file(GLOB EXAMPLE CONFIGURE_DEPENDS "*.cpp")

foreach(EXAMPLE_FILE ${EXAMPLE})
# Strip file extension
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WLE)
# Add example executable
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
target_link_libraries(${EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
endforeach()
22 changes: 22 additions & 0 deletions examples/make_strings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from itertools import combinations, product

qubits = 5
weight = 2

strings = []
nontrivial_matrix_elements = list(product(["X", "Y", "Z"], repeat=weight))
for indices in combinations(range(qubits), weight): # n(n-1)/2 terms
for elements in nontrivial_matrix_elements:
pauli_string = []
for qbit in range(qubits):
for el_position, i in enumerate(indices):
if i == qbit:
pauli_string.append(elements[el_position])
break
else:
pauli_string.append("I")
strings.append("".join(pauli_string))

print(strings)

"XXIII", "XYIII", "XZIII", "YXIII", "YYIII", "YZIII", "ZXIII", "ZYIII", "ZZIII", "XIXII", "XIYII", "XIZII", "YIXII", "YIYII", "YIZII"
3 changes: 3 additions & 0 deletions include/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Summary

All internal headers are files that start with `__`, e.g. `__pauli.hpp`. These files are all included in `fast_pauli.hpp`.
Loading

0 comments on commit 08f0c24

Please sign in to comment.