Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add clifford simulator(#1) #127

Merged
merged 3 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ jobs:
- os-arch: win_amd64
os: windows-2019
- os-arch: macosx_x86_64
os: macos-11
os: macos-13
- os-arch: macosx_arm64
os: macos-11
os: macos-13
runs-on: ${{ matrix.os }}

env:
Expand Down
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CUDA_ARCHITECTURES 70;75;80;90)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(SKBUILD)

execute_process(
Expand Down Expand Up @@ -46,7 +47,7 @@ include(ExternalProject)
ExternalProject_Add(Eigen3
PREFIX ${EIGEN3_ROOT}
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.3.9
GIT_TAG 3.4

CONFIGURE_COMMAND ""
BUILD_COMMAND ""
Expand All @@ -56,7 +57,6 @@ ExternalProject_Add(Eigen3
)
list (APPEND PRJ_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR})


find_package(pybind11 CONFIG)
list (APPEND PRJ_INCLUDE_DIRS ${PYBIND11_INCLUDE_DIR})

Expand All @@ -74,7 +74,7 @@ if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_HOST_SYSTEM_PROCESSOR
endif()
endif()

list (APPEND PRJ_INCLUDE_DIRS src/qfvm)
list (APPEND PRJ_INCLUDE_DIRS src/qfvm src/qfvm_clifford)
pybind11_add_module(${PROJECT_NAME} MODULE src/${PROJECT_NAME}/${PROJECT_NAME}.cpp)
add_dependencies(${PROJECT_NAME} Eigen3) #must add dependence for ninja
target_compile_options(${PROJECT_NAME} PUBLIC ${PRJ_COMPILE_OPTIONS})
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ archs = ["x86_64"]


[tool.cibuildwheel.macos]
environment = {MACOSX_DEPLOYMENT_TARGET = "13.6"}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for some new features used in cpp code

archs = ["x86_64", "arm64"]

repair-wheel-command = [
Expand Down
9 changes: 8 additions & 1 deletion quafu/results/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,21 @@ class SimuResult(Result):
"""

def __init__(self, input, input_form, count_dict: dict = None):
self.num = int(np.log2(input.shape[0]))
if input_form != "count_dict":
self.num = int(np.log2(input.shape[0]))
else:
# input is num qubits
self.num = input
if input_form == "density_matrix":
self.rho = np.array(input)
self.probabilities = np.diag(input)
elif input_form == "probabilities":
self.probabilities = input
elif input_form == "state_vector":
self.state_vector = input
elif input_form == "count_dict":
# do nothing, only count dict
pass
# come form c++ simulator
# TODO: add count for py_simu
if count_dict is not None:
Expand Down
12 changes: 12 additions & 0 deletions quafu/simulators/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def simulate(
if use_gpu:
if qc.executable_on_backend == False:
raise QuafuError("classical operation only support for `qfvm_qasm`")

if use_custatevec:
try:
from .qfvm import simulate_circuit_custate
Expand All @@ -101,6 +102,14 @@ def simulate(
else:
count_dict, psi = simulate_circuit(qc, psi, shots)

elif simulator == "qfvm_clifford":
try:
from .qfvm import simulate_circuit_clifford
except ImportError:
raise QuafuError("you are not using the clifford version of pyquafu")

count_dict = simulate_circuit_clifford(qc, shots)

elif simulator == "py_simu":
if qc.executable_on_backend == False:
raise QuafuError("classical operation only support for `qfvm_qasm`")
Expand Down Expand Up @@ -129,6 +138,9 @@ def simulate(
elif output == "state_vector":
return SimuResult(psi, output, count_dict)

elif output == "count_dict":
return SimuResult(max(qc.used_qubits) + 1, output, count_dict)

else:
raise ValueError(
"output should in be 'density_matrix', 'probabilities', or 'state_vector'"
Expand Down
87 changes: 87 additions & 0 deletions src/qfvm/qfvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <random>
#include <tuple>

#ifdef _USE_GPU
#include <cuda_simulator.cuh>
#endif
Expand All @@ -11,6 +13,12 @@
#include <custate_simu.cuh>
#endif

#ifdef USE_SIMD
constexpr size_t _word_size = 256;
#else
constexpr size_t _word_size = 64;
#endif

namespace py = pybind11;

template <typename T>
Expand Down Expand Up @@ -115,6 +123,82 @@ simulate_circuit(py::object const& pycircuit,
return std::make_pair(outcount, np_inputstate);
}

std::map<uint, uint> simulate_circuit_clifford(py::object const& pycircuit,
const int& shots) {

auto circuit = Circuit(pycircuit);

// If measure all at the end, simulate once
uint actual_shots = shots;

// qbit, cbit
vector<std::pair<uint, uint>> measures = circuit.measure_vec();
std::map<uint, bool> cbit_measured;
for (auto& pair : measures) {
cbit_measured[pair.second] = true;
}

// Store outcome's count
std::map<uint, uint> outcount;

circuit_simulator<_word_size> cs(circuit.qubit_num());

for (uint i = 0; i < actual_shots; i++) {

simulate(circuit, cs);
uint outcome = 0;

if (!circuit.final_measure()) {
// qubit, cbit, measure result
auto measure_results = cs.current_measurement_record();

// make sure the order is the same with other simulators
std::sort(
measure_results.begin(), measure_results.end(),
[](auto& a, auto& b) { return std::get<1>(a) < std::get<1>(b); });

for (auto& measure_result : measure_results) {
outcome *= 2;
outcome += std::get<2>(measure_result);
}

} else if (circuit.final_measure() && !measures.empty()) {
for (auto& measure : measures) {
cs.do_circuit_instruction(
{"measure", std::vector<size_t>{measure.first},
std::vector<double>{static_cast<double>(measure.second)}});
}

// qubit, cbit, measure result
auto measure_results = cs.current_measurement_record();

// make sure the order is the same with other simulators
std::sort(
measure_results.begin(), measure_results.end(),
[](auto& a, auto& b) { return std::get<1>(a) < std::get<1>(b); });

for (auto& measure_result : measure_results) {
outcome *= 2;
outcome += std::get<2>(measure_result);
}
}

if (measures.empty()) {
continue;
}

if (outcount.find(outcome) != outcount.end())
outcount[outcome]++;
else
outcount[outcome] = 1;

cs.reset_tableau();
cs.sim_record.clear();
}

return outcount;
}

#ifdef _USE_GPU
py::object simulate_circuit_gpu(py::object const& pycircuit,
py::array_t<complex<double>>& np_inputstate) {
Expand Down Expand Up @@ -164,6 +248,9 @@ PYBIND11_MODULE(qfvm, m) {
py::arg("circuit"),
py::arg("inputstate") = py::array_t<complex<double>>(0),
py::arg("shots"));
m.def("simulate_circuit_clifford", &simulate_circuit_clifford,
"Simulate with circuit using clifford", py::arg("circuit"),
py::arg("shots"));

#ifdef _USE_GPU
m.def("simulate_circuit_gpu", &simulate_circuit_gpu, "Simulate with circuit",
Expand Down
46 changes: 46 additions & 0 deletions src/qfvm/simulator.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#pragma once

#include "circuit.hpp"
#include "clifford_simulator.h"
#include "qasm.hpp"
#include "statevector.hpp"
#include "types.hpp"
#include <cstddef>
#include <vector>

void apply_op(QuantumOperator& op, StateVector<data_t>& state) {
bool matched = false;
Expand Down Expand Up @@ -114,3 +119,44 @@ void simulate(Circuit const& circuit, StateVector<data_t>& state) {
apply_op(op, state);
}
}

template <size_t word_size>
void apply_measure(circuit_simulator<word_size>& cs, const vector<pos_t>& qbits,
const vector<pos_t>& cbits) {
for (size_t i = 0; i < qbits.size(); i++) {
cs.do_circuit_instruction(
{"measure", std::vector<size_t>{qbits[i]},
std::vector<double>{static_cast<double>(cbits[i])}});
}
}

template <size_t word_size>
void apply_op(QuantumOperator& op, circuit_simulator<word_size>& cs) {
// TODO: support args
switch (OPMAP[op.name()]) {
case Opname::measure:
apply_measure(cs, op.qbits(), op.cbits());
break;
case Opname::reset:
for (auto qubit : op.qbits()) {
cs.do_circuit_instruction(
{"reset", std::vector<size_t>{static_cast<size_t>(qubit)}});
}
break;
default:
auto qubits = op.positions();
cs.do_circuit_instruction(
{op.name(), std::vector<size_t>(qubits.begin(), qubits.end())});
}
}

template <size_t word_size>
void simulate(Circuit const& circuit, circuit_simulator<word_size>& cs) {
// skip measure and handle it in qfvm.cpp
bool skip_measure = circuit.final_measure();
for (auto op : circuit.instructions()) {
if (skip_measure == true && op.name() == "measure")
continue;
apply_op(op, cs);
}
}
55 changes: 55 additions & 0 deletions src/qfvm_clifford/bit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#ifndef BIT_H_
#define BIT_H_

#include <cstddef>
#include <cstdint>

// bit in byte
struct bit {
uint8_t* byte;
uint8_t byte_index;

bit(void* ptr, size_t offset)
: byte(((uint8_t*)ptr + (offset / 8))), byte_index(offset & 7) {}

// copy assignment for bit in byte
inline bit& operator=(bool value) {
// make bit be 0
*byte &= ~((uint8_t)1 << byte_index);
// assignment
*byte |= uint8_t(value) << byte_index;
return *this;
}

inline bit& operator=(const bit& other) {
*this = bool(other);
return *this;
}

// bit operator
inline bit& operator^=(bool value) {
*byte ^= uint8_t(value) << byte_index;
return *this;
}

inline bit& operator&=(bool value) {
*byte &= (uint8_t(value) << byte_index) | ~(uint8_t(1) << byte_index);
return *this;
}

inline bit& operator|=(bool value) {
*byte |= uint8_t(value) << byte_index;
return *this;
}

// conversion operator
inline operator bool() const { return (*byte >> byte_index) & 1; }

void swap(bit other) {
bool b = bool(other);
other = bool(*this);
*this = b;
}
};

#endif
Loading