diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f76d3d8..d631366c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,8 @@ cmake_minimum_required(VERSION 3.11) project(qsim) -set(CMAKE_CXX_FLAGS "-O3 -march=native -fopenmp") - -include(FetchContent) - -FetchContent_Declare( - pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11 - GIT_TAG v2.2.4 -) -FetchContent_GetProperties(pybind11) -if(NOT pybind11_POPULATED) - FetchContent_Populate(pybind11) - add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) -endif() -pybind11_add_module(qsim pybind_interface/pybind_main.cpp) +ADD_SUBDIRECTORY(pybind_interface/sse) +ADD_SUBDIRECTORY(pybind_interface/avx512) +ADD_SUBDIRECTORY(pybind_interface/avx2) +ADD_SUBDIRECTORY(pybind_interface/basic) +ADD_SUBDIRECTORY(pybind_interface/decide) diff --git a/Makefile b/Makefile index 91503146..62c0e556 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ TARGETS = qsim TESTS = run-cxx-tests CXX=g++ -CXXFLAGS = -O3 -march=native -fopenmp +CXXFLAGS = -O3 -fopenmp PYBIND11 = true export CXX diff --git a/pybind_interface/Makefile b/pybind_interface/Makefile index 4aeb22c8..8e48ff57 100644 --- a/pybind_interface/Makefile +++ b/pybind_interface/Makefile @@ -1,13 +1,29 @@ -# The name of the shared library that results after compiling qsim for Pybind11 -QSIMLIB = ../qsimcirq/qsim`python3-config --extension-suffix` +# The names of the shared libraries that result after compiling qsim for Pybind11 +QSIMLIB_BASIC = ../qsimcirq/qsim_basic`python3-config --extension-suffix` +QSIMLIB_SSE = ../qsimcirq/qsim_sse`python3-config --extension-suffix` +QSIMLIB_AVX2 = ../qsimcirq/qsim_avx2`python3-config --extension-suffix` +QSIMLIB_AVX512 = ../qsimcirq/qsim_avx512`python3-config --extension-suffix` +QSIMLIB_DECIDE = ../qsimcirq/qsim_decide`python3-config --extension-suffix` -# The flags for the compilation of the Pybind11 interface -PYBINDFLAGS = -Wall -shared -std=c++17 -fPIC `python3 -m pybind11 --includes` + +# The flags for the compilation of the simd-specific Pybind11 interfaces +PYBINDFLAGS_BASIC = -Wall -shared -std=c++17 -fPIC `python3 -m pybind11 --includes` +PYBINDFLAGS_SSE = -msse4.1 -Wall -shared -std=c++17 -fPIC `python3 -m pybind11 --includes` +PYBINDFLAGS_AVX2 = -mavx2 -mfma -Wall -shared -std=c++17 -fPIC `python3 -m pybind11 --includes` +PYBINDFLAGS_AVX512 = -mavx512f -Wall -shared -std=c++17 -fPIC `python3 -m pybind11 --includes` .PHONY: pybind pybind: - $(CXX) pybind_main.cpp -o $(QSIMLIB) $(CXXFLAGS) $(PYBINDFLAGS) + $(CXX) basic/pybind_main_basic.cpp -o $(QSIMLIB_BASIC) $(CXXFLAGS) $(PYBINDFLAGS_BASIC) + $(CXX) sse/pybind_main_sse.cpp -o $(QSIMLIB_SSE) $(CXXFLAGS) $(PYBINDFLAGS_SSE) + $(CXX) avx2/pybind_main_avx2.cpp -o $(QSIMLIB_AVX2) $(CXXFLAGS) $(PYBINDFLAGS_AVX2) + $(CXX) avx512/pybind_main_avx512.cpp -o $(QSIMLIB_AVX512) $(CXXFLAGS) $(PYBINDFLAGS_AVX512) + $(CXX) decide/decide.cpp -o $(QSIMLIB_DECIDE) $(CXXFLAGS) $(PYBINDFLAGS_BASIC) .PHONY: clean clean: - -rm -f ./*.x ./*.a ./*.so ./*.mod $(QSIMLIB) \ No newline at end of file + -rm -f ./basic/*.x ./basic/*.a ./basic/*.so ./basic/*.mod $(QSIMLIB_BASIC) + -rm -f ./sse/*.x ./sse/*.a ./sse/*.so ./sse/*.mod $(QSIMLIB_SSE) + -rm -f ./avx2/*.x ./avx2/*.a ./avx2/*.so ./avx2/*.mod $(QSIMLIB_AVX2) + -rm -f ./avx512/*.x ./avx512/*.a ./avx512/*.so ./avx512/*.mod $(QSIMLIB_AVX512) + -rm -f ./decide/*.x ./decide/*.a ./decide/*.so ./decide/*.mod $(QSIMLIB_DECIDE) diff --git a/pybind_interface/avx2/CMakeLists.txt b/pybind_interface/avx2/CMakeLists.txt new file mode 100644 index 00000000..4382de95 --- /dev/null +++ b/pybind_interface/avx2/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.11) +project(qsim) + +IF (WIN32) + set(CMAKE_CXX_FLAGS "/arch:AVX2 /O2 /openmp") +ELSE() + set(CMAKE_CXX_FLAGS "-mavx2 -mfma -O3 -fopenmp") +ENDIF() + +if(APPLE) + set(CMAKE_CXX_STANDARD 14) + include_directories("/usr/local/include" "/usr/local/opt/llvm/include") + link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") +endif() + +include(FetchContent) + +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.2.4 +) +FetchContent_GetProperties(pybind11) +if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() +pybind11_add_module(qsim_avx2 pybind_main_avx2.cpp) diff --git a/pybind_interface/avx2/pybind_main_avx2.cpp b/pybind_interface/avx2/pybind_main_avx2.cpp new file mode 100644 index 00000000..29e5e81f --- /dev/null +++ b/pybind_interface/avx2/pybind_main_avx2.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pybind_main_avx2.h" + +#include "../../lib/simulator_avx.h" + +namespace qsim { + template + using Simulator = SimulatorAVX; +} + +#include "../pybind_main.cpp" diff --git a/pybind_interface/avx2/pybind_main_avx2.h b/pybind_interface/avx2/pybind_main_avx2.h new file mode 100644 index 00000000..0d837fc2 --- /dev/null +++ b/pybind_interface/avx2/pybind_main_avx2.h @@ -0,0 +1,17 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../pybind_main.h" + +PYBIND11_MODULE(qsim_avx2, m) { MODULE_BINDINGS } diff --git a/pybind_interface/avx512/CMakeLists.txt b/pybind_interface/avx512/CMakeLists.txt new file mode 100644 index 00000000..c265fe13 --- /dev/null +++ b/pybind_interface/avx512/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.11) +project(qsim) + + +IF (WIN32) + set(CMAKE_CXX_FLAGS "/arch:AVX512 /O2 /openmp") +ELSE() + set(CMAKE_CXX_FLAGS "-mavx512f -O3 -fopenmp") +ENDIF() + +if(APPLE) + set(CMAKE_CXX_STANDARD 14) + include_directories("/usr/local/include" "/usr/local/opt/llvm/include") + link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") +endif() + +include(FetchContent) + +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.2.4 +) +FetchContent_GetProperties(pybind11) +if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() +pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) diff --git a/pybind_interface/avx512/pybind_main_avx512.cpp b/pybind_interface/avx512/pybind_main_avx512.cpp new file mode 100644 index 00000000..97c50117 --- /dev/null +++ b/pybind_interface/avx512/pybind_main_avx512.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pybind_main_avx512.h" + +#include "../../lib/simulator_avx512.h" + +namespace qsim { + template + using Simulator = SimulatorAVX512; +} + +#include "../pybind_main.cpp" diff --git a/pybind_interface/avx512/pybind_main_avx512.h b/pybind_interface/avx512/pybind_main_avx512.h new file mode 100644 index 00000000..a27bceb4 --- /dev/null +++ b/pybind_interface/avx512/pybind_main_avx512.h @@ -0,0 +1,17 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../pybind_main.h" + +PYBIND11_MODULE(qsim_avx512, m) { MODULE_BINDINGS } diff --git a/pybind_interface/basic/CMakeLists.txt b/pybind_interface/basic/CMakeLists.txt new file mode 100644 index 00000000..9380f02d --- /dev/null +++ b/pybind_interface/basic/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.11) +project(qsim) + +IF (WIN32) + set(CMAKE_CXX_FLAGS "/O2 /openmp") +ELSE() + set(CMAKE_CXX_FLAGS "-O3 -fopenmp") +ENDIF() + + +if(APPLE) + set(CMAKE_CXX_STANDARD 14) + include_directories("/usr/local/include" "/usr/local/opt/llvm/include") + link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") +endif() + +include(FetchContent) + +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.2.4 +) +FetchContent_GetProperties(pybind11) +if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() +pybind11_add_module(qsim_basic pybind_main_basic.cpp) diff --git a/pybind_interface/basic/pybind_main_basic.cpp b/pybind_interface/basic/pybind_main_basic.cpp new file mode 100644 index 00000000..afeba392 --- /dev/null +++ b/pybind_interface/basic/pybind_main_basic.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pybind_main_basic.h" + +#include "../../lib/simulator_basic.h" + +namespace qsim { + template + using Simulator = SimulatorBasic; +} + +#include "../pybind_main.cpp" diff --git a/pybind_interface/basic/pybind_main_basic.h b/pybind_interface/basic/pybind_main_basic.h new file mode 100644 index 00000000..f6f463f5 --- /dev/null +++ b/pybind_interface/basic/pybind_main_basic.h @@ -0,0 +1,17 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../pybind_main.h" + +PYBIND11_MODULE(qsim_basic, m) { MODULE_BINDINGS } diff --git a/pybind_interface/decide/CMakeLists.txt b/pybind_interface/decide/CMakeLists.txt new file mode 100644 index 00000000..b7d28714 --- /dev/null +++ b/pybind_interface/decide/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.11) +project(qsim) + +IF (WIN32) + set(CMAKE_CXX_FLAGS "/O2 /openmp") +ELSE() + set(CMAKE_CXX_FLAGS "-O3 -fopenmp") +ENDIF() + +if(APPLE) + set(CMAKE_CXX_STANDARD 14) + include_directories("/usr/local/include" "/usr/local/opt/llvm/include") + link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") +endif() + +include(FetchContent) + +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.2.4 +) +FetchContent_GetProperties(pybind11) +if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() +pybind11_add_module(qsim_decide decide.cpp) diff --git a/pybind_interface/decide/decide.cpp b/pybind_interface/decide/decide.cpp new file mode 100644 index 00000000..2fb41848 --- /dev/null +++ b/pybind_interface/decide/decide.cpp @@ -0,0 +1,53 @@ +#include + +namespace py = pybind11; + +#ifdef _WIN32 +// Windows +#include +#define cpuid(info, x) __cpuidex(info, x, 0) + +#else +// GCC Intrinsics +#include +void cpuid(int info[4], int infoType){ + __cpuid_count(infoType, 0, info[0], info[1], info[2], info[3]); +} + +#endif + +enum Instructions { AVX512F = 0, AVX2 = 1, SSE4_1 = 2, BASIC = 3}; + +int detect_instructions() { + Instructions instr = BASIC; + int info[4]; + + cpuid(info, 0); + + int nIds = info[0]; + if (nIds >= 1) { + cpuid(info, 1); + if ((info[2] & (1 << 19)) != 0) { + instr = SSE4_1; + } + } + if (nIds >= 7) { + cpuid(info, 7); + if ((info[1] & (1 << 5) )!= 0) { + instr = AVX2; + } + if ((info[1] & (1 << 16)) != 0) { + instr = AVX512F; + } + + } + + return static_cast(instr); +} + +PYBIND11_MODULE(qsim_decide, m) { + m.doc() = "pybind11 plugin"; // optional module docstring + + // Methods for returning amplitudes + m.def("detect_instructions", &detect_instructions, "Detect SIMD"); +} diff --git a/pybind_interface/pybind_main.cpp b/pybind_interface/pybind_main.cpp index 1e76af35..37e6a20e 100644 --- a/pybind_interface/pybind_main.cpp +++ b/pybind_interface/pybind_main.cpp @@ -30,7 +30,6 @@ #include "../lib/qtrajectory.h" #include "../lib/run_qsim.h" #include "../lib/run_qsimh.h" -#include "../lib/simmux.h" #include "../lib/util.h" using namespace qsim; diff --git a/pybind_interface/pybind_main.h b/pybind_interface/pybind_main.h index c3b547ec..8de3d455 100644 --- a/pybind_interface/pybind_main.h +++ b/pybind_interface/pybind_main.h @@ -134,166 +134,163 @@ std::vector> qtrajectory_simulate_expectation_values( // Hybrid simulator. std::vector> qsimh_simulate(const py::dict &options); -PYBIND11_MODULE(qsim, m) { - m.doc() = "pybind11 plugin"; // optional module docstring - - // Methods for returning amplitudes - m.def("qsim_simulate", &qsim_simulate, "Call the qsim simulator"); - m.def("qtrajectory_simulate", &qtrajectory_simulate, - "Call the qtrajectory simulator"); - - // Methods for returning full state - m.def("qsim_simulate_fullstate", - static_cast(*)(const py::dict&, uint64_t)>( - &qsim_simulate_fullstate), - "Call the qsim simulator for full state vector simulation"); - m.def("qsim_simulate_fullstate", - static_cast(*)(const py::dict&, - const py::array_t&)>( - &qsim_simulate_fullstate), - "Call the qsim simulator for full state vector simulation"); - - m.def("qtrajectory_simulate_fullstate", - static_cast(*)(const py::dict&, uint64_t)>( - &qtrajectory_simulate_fullstate), - "Call the qtrajectory simulator for full state vector simulation"); - m.def("qtrajectory_simulate_fullstate", - static_cast(*)(const py::dict&, - const py::array_t&)>( - &qtrajectory_simulate_fullstate), - "Call the qtrajectory simulator for full state vector simulation"); - - // Methods for returning samples - m.def("qsim_sample", &qsim_sample, "Call the qsim sampler"); - m.def("qtrajectory_sample", &qtrajectory_sample, - "Call the qtrajectory sampler"); - - using GateCirq = qsim::Cirq::GateCirq; - using OpString = qsim::OpString; - - // Methods for returning expectation values - m.def("qsim_simulate_expectation_values", - static_cast>(*)( - const py::dict&, - const std::vector, unsigned>>&, - uint64_t)>( - &qsim_simulate_expectation_values), - "Call the qsim simulator for expectation value simulation"); - m.def("qsim_simulate_expectation_values", - static_cast>(*)( - const py::dict&, - const std::vector, unsigned>>&, - const py::array_t&)>( - &qsim_simulate_expectation_values), - "Call the qsim simulator for expectation value simulation"); - - m.def("qtrajectory_simulate_expectation_values", - static_cast>(*)( - const py::dict&, - const std::vector, unsigned>>&, - uint64_t)>( - &qtrajectory_simulate_expectation_values), - "Call the qtrajectory simulator for expectation value simulation"); - m.def("qtrajectory_simulate_expectation_values", - static_cast>(*)( - const py::dict&, - const std::vector, unsigned>>&, - const py::array_t&)>( - &qtrajectory_simulate_expectation_values), - "Call the qtrajectory simulator for expectation value simulation"); - - // Method for hybrid simulation - m.def("qsimh_simulate", &qsimh_simulate, "Call the qsimh simulator"); - - using GateKind = qsim::Cirq::GateKind; - using Circuit = qsim::Circuit; - using NoisyCircuit = qsim::NoisyCircuit; - - py::class_(m, "Circuit") - .def(py::init<>()) - .def_readwrite("num_qubits", &Circuit::num_qubits) - .def_readwrite("gates", &Circuit::gates); - - py::class_(m, "NoisyCircuit") - .def(py::init<>()) - .def_readwrite("num_qubits", &NoisyCircuit::num_qubits) - .def_readwrite("channels", &NoisyCircuit::channels); - - py::class_(m, "OpString") - .def(py::init<>()) - .def_readwrite("weight", &OpString::weight) - .def_readwrite("ops", &OpString::ops); - - py::enum_(m, "GateKind") - .value("kI1", GateKind::kI1) - .value("kI2", GateKind::kI2) - .value("kI", GateKind::kI) - .value("kXPowGate", GateKind::kXPowGate) - .value("kYPowGate", GateKind::kYPowGate) - .value("kZPowGate", GateKind::kZPowGate) - .value("kHPowGate", GateKind::kHPowGate) - .value("kCZPowGate", GateKind::kCZPowGate) - .value("kCXPowGate", GateKind::kCXPowGate) - .value("krx", GateKind::krx) - .value("kry", GateKind::kry) - .value("krz", GateKind::krz) - .value("kH", GateKind::kH) - .value("kS", GateKind::kS) - .value("kCZ", GateKind::kCZ) - .value("kCX", GateKind::kCX) - .value("kT", GateKind::kT) - .value("kX", GateKind::kX) - .value("kY", GateKind::kY) - .value("kZ", GateKind::kZ) - .value("kPhasedXPowGate", GateKind::kPhasedXPowGate) - .value("kPhasedXZGate", GateKind::kPhasedXZGate) - .value("kXXPowGate", GateKind::kXXPowGate) - .value("kYYPowGate", GateKind::kYYPowGate) - .value("kZZPowGate", GateKind::kZZPowGate) - .value("kXX", GateKind::kXX) - .value("kYY", GateKind::kYY) - .value("kZZ", GateKind::kZZ) - .value("kSwapPowGate", GateKind::kSwapPowGate) - .value("kISwapPowGate", GateKind::kISwapPowGate) - .value("kriswap", GateKind::kriswap) - .value("kSWAP", GateKind::kSWAP) - .value("kISWAP", GateKind::kISWAP) - .value("kPhasedISwapPowGate", GateKind::kPhasedISwapPowGate) - .value("kgivens", GateKind::kgivens) - .value("kFSimGate", GateKind::kFSimGate) - .value("kTwoQubitDiagonalGate", GateKind::kTwoQubitDiagonalGate) - .value("kThreeQubitDiagonalGate", GateKind::kThreeQubitDiagonalGate) - .value("kCCZPowGate", GateKind::kCCZPowGate) - .value("kCCXPowGate", GateKind::kCCXPowGate) - .value("kCSwapGate", GateKind::kCSwapGate) - .value("kCCZ", GateKind::kCCZ) - .value("kCCX", GateKind::kCCX) - .value("kMatrixGate", GateKind::kMatrixGate) - .value("kMeasurement", GateKind::kMeasurement) - .export_values(); - - m.def("add_gate", &add_gate, "Adds a gate to the given circuit."); - m.def("add_diagonal_gate", &add_diagonal_gate, - "Adds a two- or three-qubit diagonal gate to the given circuit."); - m.def("add_matrix_gate", &add_matrix_gate, - "Adds a matrix-defined gate to the given circuit."); - m.def("control_last_gate", &control_last_gate, - "Applies controls to the final gate of a circuit."); - - m.def("add_gate_channel", &add_gate_channel, - "Adds a gate to the given noisy circuit."); - m.def("add_diagonal_gate_channel", &add_diagonal_gate_channel, - "Adds a two- or three-qubit diagonal gate to the given noisy circuit."); - m.def("add_matrix_gate_channel", &add_matrix_gate_channel, - "Adds a matrix-defined gate to the given noisy circuit."); - m.def("control_last_gate_channel", &control_last_gate_channel, - "Applies controls to the final channel of a noisy circuit."); - - m.def("add_channel", &add_channel, - "Adds a channel to the given noisy circuit."); - - m.def("add_gate_to_opstring", &add_gate_to_opstring, - "Adds a gate to the given opstring."); -} - +#define MODULE_BINDINGS \ + m.doc() = "pybind11 plugin"; /* optional module docstring */ \ + /* Methods for returning amplitudes */ \ + m.def("qsim_simulate", &qsim_simulate, "Call the qsim simulator"); \ + m.def("qtrajectory_simulate", &qtrajectory_simulate, \ + "Call the qtrajectory simulator"); \ + \ + /* Methods for returning full state */ \ + m.def("qsim_simulate_fullstate", \ + static_cast(*)(const py::dict&, uint64_t)>( \ + &qsim_simulate_fullstate), \ + "Call the qsim simulator for full state vector simulation"); \ + m.def("qsim_simulate_fullstate", \ + static_cast(*)(const py::dict&, \ + const py::array_t&)>( \ + &qsim_simulate_fullstate), \ + "Call the qsim simulator for full state vector simulation"); \ + \ + m.def("qtrajectory_simulate_fullstate", \ + static_cast(*)(const py::dict&, uint64_t)>( \ + &qtrajectory_simulate_fullstate), \ + "Call the qtrajectory simulator for full state vector simulation"); \ + m.def("qtrajectory_simulate_fullstate", \ + static_cast(*)(const py::dict&, \ + const py::array_t&)>( \ + &qtrajectory_simulate_fullstate), \ + "Call the qtrajectory simulator for full state vector simulation"); \ + \ + /* Methods for returning samples */ \ + m.def("qsim_sample", &qsim_sample, "Call the qsim sampler"); \ + m.def("qtrajectory_sample", &qtrajectory_sample, \ + "Call the qtrajectory sampler"); \ + \ + using GateCirq = qsim::Cirq::GateCirq; \ + using OpString = qsim::OpString; \ + \ + /* Methods for returning expectation values */ \ + m.def("qsim_simulate_expectation_values", \ + static_cast>(*)( \ + const py::dict&, \ + const std::vector, unsigned>>&, \ + uint64_t)>( \ + &qsim_simulate_expectation_values), \ + "Call the qsim simulator for expectation value simulation"); \ + m.def("qsim_simulate_expectation_values", \ + static_cast>(*)( \ + const py::dict&, \ + const std::vector, unsigned>>&, \ + const py::array_t&)>( \ + &qsim_simulate_expectation_values), \ + "Call the qsim simulator for expectation value simulation"); \ + \ + m.def("qtrajectory_simulate_expectation_values", \ + static_cast>(*)( \ + const py::dict&, \ + const std::vector, unsigned>>&, \ + uint64_t)>( \ + &qtrajectory_simulate_expectation_values), \ + "Call the qtrajectory simulator for expectation value simulation"); \ + m.def("qtrajectory_simulate_expectation_values", \ + static_cast>(*)( \ + const py::dict&, \ + const std::vector, unsigned>>&, \ + const py::array_t&)>( \ + &qtrajectory_simulate_expectation_values), \ + "Call the qtrajectory simulator for expectation value simulation"); \ + \ + /* Method for hybrid simulation */ \ + m.def("qsimh_simulate", &qsimh_simulate, "Call the qsimh simulator"); \ + \ + using GateKind = qsim::Cirq::GateKind; \ + using Circuit = qsim::Circuit; \ + using NoisyCircuit = qsim::NoisyCircuit; \ + \ + py::class_(m, "Circuit") \ + .def(py::init<>()) \ + .def_readwrite("num_qubits", &Circuit::num_qubits) \ + .def_readwrite("gates", &Circuit::gates); \ + \ + py::class_(m, "NoisyCircuit") \ + .def(py::init<>()) \ + .def_readwrite("num_qubits", &NoisyCircuit::num_qubits) \ + .def_readwrite("channels", &NoisyCircuit::channels); \ + \ + py::class_(m, "OpString") \ + .def(py::init<>()) \ + .def_readwrite("weight", &OpString::weight) \ + .def_readwrite("ops", &OpString::ops); \ + \ + py::enum_(m, "GateKind") \ + .value("kI1", GateKind::kI1) \ + .value("kI2", GateKind::kI2) \ + .value("kI", GateKind::kI) \ + .value("kXPowGate", GateKind::kXPowGate) \ + .value("kYPowGate", GateKind::kYPowGate) \ + .value("kZPowGate", GateKind::kZPowGate) \ + .value("kHPowGate", GateKind::kHPowGate) \ + .value("kCZPowGate", GateKind::kCZPowGate) \ + .value("kCXPowGate", GateKind::kCXPowGate) \ + .value("krx", GateKind::krx) \ + .value("kry", GateKind::kry) \ + .value("krz", GateKind::krz) \ + .value("kH", GateKind::kH) \ + .value("kS", GateKind::kS) \ + .value("kCZ", GateKind::kCZ) \ + .value("kCX", GateKind::kCX) \ + .value("kT", GateKind::kT) \ + .value("kX", GateKind::kX) \ + .value("kY", GateKind::kY) \ + .value("kZ", GateKind::kZ) \ + .value("kPhasedXPowGate", GateKind::kPhasedXPowGate) \ + .value("kPhasedXZGate", GateKind::kPhasedXZGate) \ + .value("kXXPowGate", GateKind::kXXPowGate) \ + .value("kYYPowGate", GateKind::kYYPowGate) \ + .value("kZZPowGate", GateKind::kZZPowGate) \ + .value("kXX", GateKind::kXX) \ + .value("kYY", GateKind::kYY) \ + .value("kZZ", GateKind::kZZ) \ + .value("kSwapPowGate", GateKind::kSwapPowGate) \ + .value("kISwapPowGate", GateKind::kISwapPowGate) \ + .value("kriswap", GateKind::kriswap) \ + .value("kSWAP", GateKind::kSWAP) \ + .value("kISWAP", GateKind::kISWAP) \ + .value("kPhasedISwapPowGate", GateKind::kPhasedISwapPowGate) \ + .value("kgivens", GateKind::kgivens) \ + .value("kFSimGate", GateKind::kFSimGate) \ + .value("kTwoQubitDiagonalGate", GateKind::kTwoQubitDiagonalGate) \ + .value("kThreeQubitDiagonalGate", GateKind::kThreeQubitDiagonalGate) \ + .value("kCCZPowGate", GateKind::kCCZPowGate) \ + .value("kCCXPowGate", GateKind::kCCXPowGate) \ + .value("kCSwapGate", GateKind::kCSwapGate) \ + .value("kCCZ", GateKind::kCCZ) \ + .value("kCCX", GateKind::kCCX) \ + .value("kMatrixGate", GateKind::kMatrixGate) \ + .value("kMeasurement", GateKind::kMeasurement) \ + .export_values(); \ + \ + m.def("add_gate", &add_gate, "Adds a gate to the given circuit."); \ + m.def("add_diagonal_gate", &add_diagonal_gate, \ + "Adds a two- or three-qubit diagonal gate to the given circuit."); \ + m.def("add_matrix_gate", &add_matrix_gate, \ + "Adds a matrix-defined gate to the given circuit."); \ + m.def("control_last_gate", &control_last_gate, \ + "Applies controls to the final gate of a circuit."); \ + \ + m.def("add_gate_channel", &add_gate_channel, \ + "Adds a gate to the given noisy circuit."); \ + m.def("add_diagonal_gate_channel", &add_diagonal_gate_channel, \ + "Adds a two- or three-qubit diagonal gate to the given noisy circuit."); \ + m.def("add_matrix_gate_channel", &add_matrix_gate_channel, \ + "Adds a matrix-defined gate to the given noisy circuit."); \ + m.def("control_last_gate_channel", &control_last_gate_channel, \ + "Applies controls to the final channel of a noisy circuit."); \ + \ + m.def("add_channel", &add_channel, \ + "Adds a channel to the given noisy circuit."); \ + \ + m.def("add_gate_to_opstring", &add_gate_to_opstring, \ + "Adds a gate to the given opstring."); #endif diff --git a/pybind_interface/sse/CMakeLists.txt b/pybind_interface/sse/CMakeLists.txt new file mode 100644 index 00000000..6fa7201f --- /dev/null +++ b/pybind_interface/sse/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.11) +project(qsim) + +IF (WIN32) + set(CMAKE_CXX_FLAGS "/O2 /openmp") +ELSE() + set(CMAKE_CXX_FLAGS "-msse4.1 -O3 -fopenmp") +ENDIF() + + +if(APPLE) + set(CMAKE_CXX_STANDARD 14) + include_directories("/usr/local/include" "/usr/local/opt/llvm/include") + link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") +endif() + +include(FetchContent) + +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v2.2.4 +) +FetchContent_GetProperties(pybind11) +if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) +endif() +pybind11_add_module(qsim_sse pybind_main_sse.cpp) diff --git a/pybind_interface/sse/pybind_main_sse.cpp b/pybind_interface/sse/pybind_main_sse.cpp new file mode 100644 index 00000000..0ff8c731 --- /dev/null +++ b/pybind_interface/sse/pybind_main_sse.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pybind_main_sse.h" + +#include "../../lib/simulator_sse.h" + +namespace qsim { + template + using Simulator = SimulatorSSE; +} + +#include "../pybind_main.cpp" diff --git a/pybind_interface/sse/pybind_main_sse.h b/pybind_interface/sse/pybind_main_sse.h new file mode 100644 index 00000000..f3726350 --- /dev/null +++ b/pybind_interface/sse/pybind_main_sse.h @@ -0,0 +1,17 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../pybind_main.h" + +PYBIND11_MODULE(qsim_sse, m) { MODULE_BINDINGS } diff --git a/qsimcirq/__init__.py b/qsimcirq/__init__.py index 5091f093..d0518841 100644 --- a/qsimcirq/__init__.py +++ b/qsimcirq/__init__.py @@ -1,3 +1,22 @@ +import importlib +from qsimcirq import qsim_decide + + +def _load_simd_qsim(): + instr = qsim_decide.detect_instructions() + if instr == 0: + qsim = importlib.import_module("qsimcirq.qsim_avx512") + elif instr == 1: + qsim = importlib.import_module("qsimcirq.qsim_avx2") + elif instr == 2: + qsim = importlib.import_module("qsimcirq.qsim_sse") + else: + qsim = importlib.import_module("qsimcirq.qsim_basic") + return qsim + + +qsim = _load_simd_qsim() + from .qsim_circuit import add_op_to_opstring, add_op_to_circuit, QSimCircuit from .qsim_simulator import QSimSimulatorState, QSimSimulatorTrialResult, QSimSimulator from .qsimh_simulator import QSimhSimulator diff --git a/qsimcirq/qsim_circuit.py b/qsimcirq/qsim_circuit.py index 194dc497..4d63fcd6 100644 --- a/qsimcirq/qsim_circuit.py +++ b/qsimcirq/qsim_circuit.py @@ -16,7 +16,7 @@ import warnings import cirq -from qsimcirq import qsim +from . import qsim from typing import Dict, Union diff --git a/qsimcirq/qsim_simulator.py b/qsimcirq/qsim_simulator.py index ee232ca5..886334c7 100644 --- a/qsimcirq/qsim_simulator.py +++ b/qsimcirq/qsim_simulator.py @@ -32,7 +32,7 @@ import numpy as np -from qsimcirq import qsim +from . import qsim import qsimcirq.qsim_circuit as qsimc diff --git a/qsimcirq/qsimh_simulator.py b/qsimcirq/qsimh_simulator.py index 92d5d62d..3d2ec049 100644 --- a/qsimcirq/qsimh_simulator.py +++ b/qsimcirq/qsimh_simulator.py @@ -16,7 +16,7 @@ from cirq import study, ops, protocols, circuits, value, SimulatesAmplitudes -from qsimcirq import qsim +from . import qsim import qsimcirq.qsim_circuit as qsimc diff --git a/setup.py b/setup.py index 23002fbb..9fea30da 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,12 @@ def build_extension(self, ext): cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] build_args += ["--", "-j2"] + if platform.system() == "Darwin": + cmake_args += [ + "-DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang", + "-DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++", + ] + env = os.environ.copy() env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( env.get("CXXFLAGS", ""), self.distribution.get_version() @@ -91,7 +97,13 @@ def build_extension(self, ext): description=description, long_description=long_description, long_description_content_type="text/markdown", - ext_modules=[CMakeExtension("qsimcirq/qsim")], + ext_modules=[ + CMakeExtension("qsimcirq/qsim_avx512"), + CMakeExtension("qsimcirq/qsim_avx2"), + CMakeExtension("qsimcirq/qsim_sse"), + CMakeExtension("qsimcirq/qsim_basic"), + CMakeExtension("qsimcirq/qsim_decide"), + ], cmdclass=dict(build_ext=CMakeBuild), zip_safe=False, packages=["qsimcirq"],