diff --git a/.devcontainer/cpu/devcontainer.json b/.devcontainer/cpu/devcontainer.json index 996874b0..6ecddc8c 100644 --- a/.devcontainer/cpu/devcontainer.json +++ b/.devcontainer/cpu/devcontainer.json @@ -31,8 +31,9 @@ "C_Cpp.default.includePath": [ "scaluq", "/usr/local/include/kokkos", - "build/_deps/eigen_fetch-src", - "build/_deps/googletest_fetch-src/googletest/include", + "build/_deps/eigen-src", + "build/_deps/googletest-src/googletest/include", + "build/_deps/json-src/include", "~/.local/lib/python3.10/site-packages/nanobind/include", "/usr/include/python3.10" ], diff --git a/.devcontainer/gpu/devcontainer.json b/.devcontainer/gpu/devcontainer.json index cdce95a3..818f3dbb 100644 --- a/.devcontainer/gpu/devcontainer.json +++ b/.devcontainer/gpu/devcontainer.json @@ -35,6 +35,7 @@ "/usr/local/include/kokkos", "build/_deps/eigen-src", "build/_deps/googletest-src/googletest/include", + "build/_deps/json-src/include", "~/.local/lib/python3.10/site-packages/nanobind/include", "/usr/include/python3.10" ], diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index b12f511b..f447f5ec 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -88,7 +88,7 @@ jobs: cd example_project/ cmake -B build/ -D "CMAKE_C_COMPILER=$CMAKE_C_COMPILER" -D "CMAKE_CXX_COMPILER=$CMAKE_CXX_COMPILER" -D "SCALUQ_USE_CUDA=$SCALUQ_USE_CUDA" make -C build - if [ "$SCALUQ_USE_CUDA" != 'ON']; then + if [ "$SCALUQ_USE_CUDA" != 'ON' ]; then build/main fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 915d617e..57501c05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,15 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(eigen) +# nlohmann_json +FetchContent_Declare( + json + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + DOWNLOAD_EXTRACT_TIMESTAMP TRUE +) +FetchContent_MakeAvailable(json) +install(DIRECTORY ${json_SOURCE_DIR}/single_include/nlohmann DESTINATION include) + # nanobind if(SKBUILD) find_package(Python 3.8 diff --git a/example_project/CMakeLists.txt b/example_project/CMakeLists.txt index 4aa54091..dd6e4553 100644 --- a/example_project/CMakeLists.txt +++ b/example_project/CMakeLists.txt @@ -9,11 +9,13 @@ endif() find_package(Kokkos) find_package(scaluq) add_executable(main main.cpp) -target_include_directories(main PUBLIC /usr/local/include/scaluq) -target_include_directories(main PUBLIC /usr/local/include/kokkos) -target_include_directories(main PUBLIC /usr/local/include/eigen3) +target_include_directories(main PUBLIC + /usr/local/include/scaluq + /usr/local/include/kokkos + /usr/local/include/eigen3 + /usr/local/include/nlohmann +) target_compile_features(main PUBLIC cxx_std_20) target_compile_options(main PUBLIC -fopenmp) target_compile_definitions(main PUBLIC OPENMP) target_link_libraries(main PUBLIC scaluq::scaluq) - diff --git a/exe/CMakeLists.txt b/exe/CMakeLists.txt index 1ece2b5d..d99525f5 100644 --- a/exe/CMakeLists.txt +++ b/exe/CMakeLists.txt @@ -6,6 +6,8 @@ function(exe name) scaluq Eigen3::Eigen Kokkos::kokkos + nlohmann_json::nlohmann_json + GTest::gtest_main ) if (SCALUQ_USE_TEST) target_link_libraries(${name} PRIVATE GTest::gtest_main) @@ -13,5 +15,4 @@ function(exe name) target_include_directories(${name} PRIVATE ${PROJECT_SOURCE_DIR}/include) endfunction() -# exe({file}) を追加すると、{file}.cpp をコンパイルして実行ファイルを build/exe/{file} として生成する exe(main) diff --git a/exe/main.cpp b/exe/main.cpp index ccd4435a..29bf8f06 100644 --- a/exe/main.cpp +++ b/exe/main.cpp @@ -1,15 +1,19 @@ #include #include -#include -#include -#include -#include +#include + +using namespace scaluq; +using namespace nlohmann; int main() { scaluq::initialize(); // must be called before using any scaluq methods { - const std::uint64_t n_qubits = 3; - scaluq::StateVector state = scaluq::StateVector::Haar_random_state(n_qubits, 0); + std::uint64_t n_qubits = 3; + scaluq::StateVector state(n_qubits); + state.load({0, 1, 2, 3, 4, 5, 6, 7}); + Json j = state; + std::cout << j << std::endl; + state = j; std::cout << state << std::endl; scaluq::Circuit circuit(n_qubits); @@ -24,5 +28,144 @@ int main() { auto value = observable.get_expectation_value(state); std::cout << value << std::endl; } - scaluq::finalize(); // must be called last + { + std::uint64_t n_qubits = 2, batch_size = 2; + scaluq::StateVectorBatched states(batch_size, n_qubits); + states.set_Haar_random_state(batch_size, n_qubits, false); + Json j = states; + std::cout << j << std::endl; + states = j; + std::cout << states << std::endl; + } + { + double coef = 2.0; + std::string pauli_string = "X 0 Z 1 Y 2"; + PauliOperator pauli(pauli_string, coef); + Json j = pauli; + std::cout << j << std::endl; + pauli = j; + } + { + std::uint64_t n_qubits = 3; + Operator op(n_qubits); + op.add_operator({0b001, 0b010, Complex(2)}); + op.add_operator({"X 2 Y 1", 1}); + Json j = op; + std::cout << j << std::endl; + op = j; + } + { + std::cout << Json(gate::I()) << std::endl; + std::cout << Json(gate::X(2, {0, 3})) << std::endl; + std::cout << Json(gate::Y(2, {0, 3})) << std::endl; + std::cout << Json(gate::Z(2, {0, 3})) << std::endl; + std::cout << Json(gate::H(2, {0, 3})) << std::endl; + std::cout << Json(gate::S(2, {0, 3})) << std::endl; + std::cout << Json(gate::Sdag(2, {0, 3})) << std::endl; + std::cout << Json(gate::T(2, {0, 3})) << std::endl; + std::cout << Json(gate::Tdag(2, {0, 3})) << std::endl; + std::cout << Json(gate::SqrtX(2, {0, 3})) << std::endl; + std::cout << Json(gate::SqrtXdag(2, {0, 3})) << std::endl; + std::cout << Json(gate::SqrtY(2, {0, 3})) << std::endl; + std::cout << Json(gate::SqrtYdag(2, {0, 3})) << std::endl; + std::cout << Json(gate::RX(2, 0.5, {0, 3})) << std::endl; + std::cout << Json(gate::RY(2, 0.5, {0, 3})) << std::endl; + std::cout << Json(gate::RZ(2, 0.5, {0, 3})) << std::endl; + std::cout << Json(gate::U1(2, 0.5, {0, 3})) << std::endl; + std::cout << Json(gate::U2(2, 0.5, 0.3, {0, 3})) << std::endl; + std::cout << Json(gate::U3(2, 0.5, 0.3, 0.1, {0, 3})) << std::endl; + std::cout << Json(gate::Swap(1, 2, {0, 3})) << std::endl; + + PauliOperator pauli("X 2 Y 1"); + std::cout << Json(gate::Pauli(pauli)) << std::endl; + std::cout << Json(gate::PauliRotation(pauli, 0.5)) << std::endl; + + std::cout << Json(gate::OneTargetMatrix(2, {0, 1, 2, 3})) << std::endl; + std::cout << Json(gate::TwoTargetMatrix( + 2, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})) + << std::endl; + + auto probgate = + gate::Probablistic({.1, .9}, {gate::X(0), gate::I()}); + std::cout << Json(probgate) << std::endl; + + std::cout << Json(gate::ParamRX(2, 1.5, {0, 3})) << std::endl; + std::cout << Json(gate::ParamRY(2, 1.5, {0, 3})) << std::endl; + std::cout << Json(gate::ParamRZ(2, 1.5, {0, 3})) << std::endl; + std::cout << Json(gate::ParamPauliRotation(pauli, 0.5)) << std::endl; + + auto paramprobgate = gate::ParamProbablistic( + {.1, .9}, {gate::ParamRX(0), gate::I()}); + std::cout << Json(paramprobgate) << std::endl; + } + { + auto x = gate::X(1, {2}); + Json j = x; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + auto x = gate::RX(1, 0.5, {2}); + Json j = x; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + auto x = gate::Swap(1, 3, {2}); + Json j = x; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + auto x = gate::OneTargetMatrix(1, {2., 3., 0., 10.}, {0, 3}); + Json j = x; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + PauliOperator pauli("X 2 Y 1"); + auto x = gate::PauliRotation(pauli, 1.5, {0, 3}); + Json j = x; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + auto probgate = + gate::Probablistic({.1, .9}, {gate::X(0, {2, 3}), gate::I()}); + Json j = probgate; + std::cout << j << std::endl; + Gate gate = j; + std::cout << gate << std::endl; + } + { + auto x = gate::ParamRX(1, {2}); + Json j = x; + std::cout << j << std::endl; + ParamGate gate = j; + std::cout << gate << std::endl; + } + { + auto paramprobgate = gate::ParamProbablistic( + {.1, .9}, {gate::ParamRX(0), gate::I()}); + Json j = paramprobgate; + std::cout << j << std::endl; + ParamGate gate = j; + std::cout << gate << std::endl; + } + { + Circuit circuit(10); + circuit.add_gate(gate::X(0, {3})); + circuit.add_param_gate(gate::ParamRX(0, 0.5, {3}), "RX"); + Json j = circuit; + std::cout << j << std::endl; + Circuit c = j; + std::cout << Json(c) << std::endl; + } + + Kokkos::finalize(); } diff --git a/exe/main.py b/exe/main.py index e67893b9..d40aa917 100644 --- a/exe/main.py +++ b/exe/main.py @@ -1,11 +1,26 @@ -from scaluq import * -# from scaluq.gate import S +from scaluq.f64 import * def main(): - state = StateVector(2) - swap_gate = gate.Swap(0, 1) - print(state) - print(swap_gate) + state = StateVector(1) + x_gate = gate.X(0, [1]) + print(state.to_json()) + print(x_gate.to_json()) + print(gate.ParamRX(0, 0.5, [1]).to_json()) + + circuit = Circuit(3) + circuit.add_gate(x_gate) + print(circuit.to_json()) + + pauli = PauliOperator("X 3 Y 2") + + operator = Operator(4) + operator.add_operator(pauli) + + print(operator.to_json()) + + states = StateVectorBatched(3, 3) + print(states.to_json()) + if __name__ == "__main__": main() diff --git a/include/scaluq/circuit/circuit.hpp b/include/scaluq/circuit/circuit.hpp index 1aa1614e..a9ccdf01 100644 --- a/include/scaluq/circuit/circuit.hpp +++ b/include/scaluq/circuit/circuit.hpp @@ -13,11 +13,12 @@ template class Circuit { public: using GateWithKey = std::variant, std::pair, std::string>>; + Circuit() = default; explicit Circuit(std::uint64_t n_qubits) : _n_qubits(n_qubits) {} [[nodiscard]] inline std::uint64_t n_qubits() const { return _n_qubits; } [[nodiscard]] inline const std::vector& gate_list() const { return _gate_list; } - [[nodiscard]] inline std::uint64_t n_gates() { return _gate_list.size(); } + [[nodiscard]] inline std::uint64_t n_gates() const { return _gate_list.size(); } [[nodiscard]] std::set key_set() const; [[nodiscard]] inline const GateWithKey& get_gate_at(std::uint64_t idx) const { if (idx >= _gate_list.size()) { @@ -25,7 +26,7 @@ class Circuit { } return _gate_list[idx]; } - [[nodiscard]] inline std::optional get_param_key_at(std::uint64_t idx) { + [[nodiscard]] inline std::optional get_param_key_at(std::uint64_t idx) const { if (idx >= _gate_list.size()) { throw std::runtime_error( "Circuit::get_parameter_key(std::uint64_t): index out of bounds"); @@ -64,6 +65,30 @@ class Circuit { Circuit get_inverse() const; + friend void to_json(Json& j, const Circuit& circuit) { + j = Json{{"n_qubits", circuit.n_qubits()}, {"gate_list", Json::array()}}; + for (auto&& gate : circuit.gate_list()) { + if (gate.index() == 0) + j["gate_list"].emplace_back(Json{{"gate", std::get<0>(gate)}}); + else + j["gate_list"].emplace_back( + Json{{"gate", std::get<1>(gate).first}, {"key", std::get<1>(gate).second}}); + } + } + + friend void from_json(const Json& j, Circuit& circuit) { + circuit = Circuit(j.at("n_qubits").get()); + const Json& tmp_list = j.at("gate_list"); + for (const Json& gate_with_key : tmp_list) { + if (gate_with_key.contains("key")) { + circuit.add_param_gate(gate_with_key.at("gate").get>(), + gate_with_key.at("key").get()); + } else { + circuit.add_gate(gate_with_key.at("gate").get>()); + } + } + } + private: std::uint64_t _n_qubits; @@ -126,7 +151,17 @@ void bind_circuit_circuit_hpp(nb::module_& m) { .def("copy", &Circuit::copy, "Copy circuit. All the gates inside is copied.") .def("get_inverse", &Circuit::get_inverse, - "Get inverse of circuit. All the gates are newly created."); + "Get inverse of circuit. All the gates are newly created.") + .def( + "to_json", + [](const Circuit& circuit) { return Json(circuit).dump(); }, + "Information as json style.") + .def( + "load_json", + [](Circuit& circuit, const std::string& str) { + circuit = nlohmann::json::parse(str); + }, + "Read an object from the JSON representation of the circuit."); } } // namespace internal #endif diff --git a/include/scaluq/gate/gate.hpp b/include/scaluq/gate/gate.hpp index 24cf5046..2ddd8e56 100644 --- a/include/scaluq/gate/gate.hpp +++ b/include/scaluq/gate/gate.hpp @@ -209,11 +209,16 @@ class GateBase : public std::enable_shared_from_this> { virtual void update_quantum_state(StateVectorBatched& states) const = 0; [[nodiscard]] virtual std::string to_string(const std::string& indent = "") const = 0; + + virtual void get_as_json(Json& j) const { j = Json{{"type", "Unknown"}}; } }; template concept GateImpl = std::derived_from>; +template +inline std::shared_ptr get_from_json(const Json&); + template class GatePtr { friend class GateFactory; @@ -276,6 +281,41 @@ class GatePtr { os << gate->to_string(); return os; } + + friend void to_json(Json& j, const GatePtr& gate) { gate->get_as_json(j); } + + friend void from_json(const Json& j, GatePtr& gate) { + std::string type = j.at("type"); + + // clang-format off + if (type == "I") gate = get_from_json>(j); + else if (type == "GlobalPhase") gate = get_from_json>(j); + else if (type == "X") gate = get_from_json>(j); + else if (type == "Y") gate = get_from_json>(j); + else if (type == "Z") gate = get_from_json>(j); + else if (type == "H") gate = get_from_json>(j); + else if (type == "S") gate = get_from_json>(j); + else if (type == "Sdag") gate = get_from_json>(j); + else if (type == "T") gate = get_from_json>(j); + else if (type == "Tdag") gate = get_from_json>(j); + else if (type == "SqrtX") gate = get_from_json>(j); + else if (type == "SqrtXdag") gate = get_from_json>(j); + else if (type == "SqrtY") gate = get_from_json>(j); + else if (type == "SqrtYdag") gate = get_from_json>(j); + else if (type == "RX") gate = get_from_json>(j); + else if (type == "RY") gate = get_from_json>(j); + else if (type == "RZ") gate = get_from_json>(j); + else if (type == "U1") gate = get_from_json>(j); + else if (type == "U2") gate = get_from_json>(j); + else if (type == "U3") gate = get_from_json>(j); + else if (type == "Swap") gate = get_from_json>(j); + else if (type == "OneTargetMatrix") gate = get_from_json>(j); + else if (type == "TwoTargetMatrix") gate = get_from_json>(j); + else if (type == "Pauli") gate = get_from_json>(j); + else if (type == "PauliRotation") gate = get_from_json>(j); + else if (type == "Probablistic") gate = get_from_json>(j); + // clang-format on + } }; } // namespace internal @@ -333,7 +373,17 @@ namespace internal { .def( \ "__str__", \ [](const GATE_TYPE& gate) { return gate->to_string(""); }, \ - "Get string representation of the gate.") + "Get string representation of the gate.") \ + .def( \ + "to_json", \ + [](const GATE_TYPE& gate) { return Json(gate).dump(); }, \ + "Get JSON representation of the gate.") \ + .def( \ + "load_json", \ + [](GATE_TYPE& gate, const std::string& str) { \ + gate = nlohmann::json::parse(str); \ + }, \ + "Read an object from the JSON representation of the gate.") template nb::class_> gate_base_def; diff --git a/include/scaluq/gate/gate_matrix.hpp b/include/scaluq/gate/gate_matrix.hpp index eb4ca027..daa79b48 100644 --- a/include/scaluq/gate/gate_matrix.hpp +++ b/include/scaluq/gate/gate_matrix.hpp @@ -7,7 +7,6 @@ #include "../constant.hpp" #include "gate.hpp" -#include "gate_standard.hpp" namespace scaluq { namespace internal { @@ -32,6 +31,13 @@ class DenseMatrixGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "DensetMatrix"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"matrix", "Not inplemented yet"}}; + } }; template @@ -56,6 +62,13 @@ class SparseMatrixGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "SparseMatrix"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"matrix", "Not inplemented yet"}}; + } }; } // namespace internal @@ -65,6 +78,55 @@ using SparseMatrixGate = internal::GatePtr>; template using DenseMatrixGate = internal::GatePtr>; +namespace internal { +#define DECLARE_GET_FROM_JSON_ONETARGETMATRIXGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + auto matrix = j.at("matrix").get>>>(); \ + return std::make_shared>( \ + vector_to_mask(targets), \ + vector_to_mask(controls), \ + std::array, 2>, 2>{ \ + matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1]}); \ + } +DECLARE_GET_FROM_JSON_ONETARGETMATRIXGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_ONETARGETMATRIXGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_ONETARGETMATRIXGATE_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_TWOTARGETMATRIXGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + auto matrix = j.at("matrix").get>>>(); \ + return std::make_shared>( \ + vector_to_mask(targets), \ + vector_to_mask(controls), \ + std::array, 4>, 4>{matrix[0][0], \ + matrix[0][1], \ + matrix[0][2], \ + matrix[0][3], \ + matrix[1][0], \ + matrix[1][1], \ + matrix[1][2], \ + matrix[1][3], \ + matrix[2][0], \ + matrix[2][1], \ + matrix[2][2], \ + matrix[2][3], \ + matrix[3][0], \ + matrix[3][1], \ + matrix[3][2], \ + matrix[3][3]}); \ + } +DECLARE_GET_FROM_JSON_TWOTARGETMATRIXGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_TWOTARGETMATRIXGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_TWOTARGETMATRIXGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/gate_pauli.hpp b/include/scaluq/gate/gate_pauli.hpp index 32626736..71157c63 100644 --- a/include/scaluq/gate/gate_pauli.hpp +++ b/include/scaluq/gate/gate_pauli.hpp @@ -30,6 +30,11 @@ class PauliGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{ + {"type", "Pauli"}, {"control", this->control_qubit_list()}, {"pauli", this->pauli()}}; + } }; template @@ -58,6 +63,13 @@ class PauliRotationGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "PauliRotation"}, + {"control", this->control_qubit_list()}, + {"pauli", this->pauli()}, + {"angle", this->angle()}}; + } }; } // namespace internal @@ -66,6 +78,29 @@ using PauliGate = internal::GatePtr>; template using PauliRotationGate = internal::GatePtr>; +namespace internal { +#define DECLARE_GET_FROM_JSON_PAULIGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto controls = j.at("control").get>(); \ + auto pauli = j.at("pauli").get>(); \ + return std::make_shared>(vector_to_mask(controls), pauli); \ + } \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto controls = j.at("control").get>(); \ + auto pauli = j.at("pauli").get>(); \ + auto angle = j.at("angle").get(); \ + return std::make_shared>( \ + vector_to_mask(controls), pauli, angle); \ + } + +DECLARE_GET_FROM_JSON_PAULIGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_PAULIGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_PAULIGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/gate_probablistic.hpp b/include/scaluq/gate/gate_probablistic.hpp index 1e67b806..5d656ccd 100644 --- a/include/scaluq/gate/gate_probablistic.hpp +++ b/include/scaluq/gate/gate_probablistic.hpp @@ -1,7 +1,9 @@ #pragma once #include "../util/random.hpp" -#include "gate.hpp" +#include "gate_matrix.hpp" +#include "gate_pauli.hpp" +#include "gate_standard.hpp" namespace scaluq { namespace internal { @@ -60,12 +62,34 @@ class ProbablisticGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Probablistic"}, + {"gate_list", this->gate_list()}, + {"distribution", this->distribution()}}; + } }; } // namespace internal template using ProbablisticGate = internal::GatePtr>; +namespace internal { + +#define DECLARE_GET_FROM_JSON_PROBGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto distribution = j.at("distribution").get>(); \ + auto gate_list = j.at("gate_list").get>>(); \ + return std::make_shared>(distribution, gate_list); \ + } + +DECLARE_GET_FROM_JSON_PROBGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_PROBGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_PROBGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/gate_standard.hpp b/include/scaluq/gate/gate_standard.hpp index bfe913cb..50dc2668 100644 --- a/include/scaluq/gate/gate_standard.hpp +++ b/include/scaluq/gate/gate_standard.hpp @@ -20,6 +20,8 @@ class IGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { j = Json{{"type", "I"}}; } }; template @@ -42,6 +44,13 @@ class GlobalPhaseGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "GlobalPhase"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"phase", this->phase()}}; + } }; template @@ -70,6 +79,12 @@ class XGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "X"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -86,6 +101,12 @@ class YGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Y"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -102,6 +123,12 @@ class ZGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Z"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -118,6 +145,12 @@ class HGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "H"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -151,6 +184,12 @@ class SGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "S"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -167,6 +206,12 @@ class SdagGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Sdag"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -183,6 +228,12 @@ class TGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "T"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -199,6 +250,12 @@ class TdagGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Tdag"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -217,6 +274,12 @@ class SqrtXGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "SqrtX"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -233,6 +296,12 @@ class SqrtXdagGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "SqrtXdag"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -251,6 +320,12 @@ class SqrtYGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "SqrtY"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -267,6 +342,12 @@ class SqrtYdagGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "SqrtYdag"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -283,6 +364,12 @@ class P0GateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "P0"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -299,6 +386,12 @@ class P1GateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "P1"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; template @@ -316,6 +409,13 @@ class RXGateImpl : public RotationGateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "RX"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"angle", this->angle()}}; + } }; template @@ -333,6 +433,13 @@ class RYGateImpl : public RotationGateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "RY"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"angle", this->angle()}}; + } }; template @@ -350,6 +457,13 @@ class RZGateImpl : public RotationGateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "RZ"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"angle", this->angle()}}; + } }; template @@ -372,6 +486,13 @@ class U1GateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "U1"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"lambda", this->lambda()}}; + } }; template class U2GateImpl : public GateBase { @@ -396,6 +517,14 @@ class U2GateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "U2"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"lambda", this->lambda()}, + {"phi", this->phi()}}; + } }; template @@ -420,6 +549,15 @@ class U3GateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "U3"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"lambda", this->lambda()}, + {"phi", this->phi()}, + {"theta", this->theta()}}; + } }; template @@ -436,6 +574,12 @@ class SwapGateImpl : public GateBase { void update_quantum_state(StateVectorBatched& states) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "Swap"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}}; + } }; } // namespace internal @@ -487,6 +631,120 @@ using U3Gate = internal::GatePtr>; template using SwapGate = internal::GatePtr>; +namespace internal { + +#define DECLARE_GET_FROM_JSON_IGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json&) { \ + return std::make_shared>(); \ + } +DECLARE_GET_FROM_JSON_IGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_IGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_IGATE_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_GLOBALPHASEGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto controls = j.at("control").get>(); \ + Type phase = j.at("phase").get(); \ + return std::make_shared>(vector_to_mask(controls), phase); \ + } +DECLARE_GET_FROM_JSON_GLOBALPHASEGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_GLOBALPHASEGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_GLOBALPHASEGATE_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_WITH_TYPE(Impl, Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + return std::make_shared>(vector_to_mask(targets), \ + vector_to_mask(controls)); \ + } +#define DECLARE_GET_FROM_JSON(Impl) \ + DECLARE_GET_FROM_JSON_WITH_TYPE(Impl, double) \ + DECLARE_GET_FROM_JSON_WITH_TYPE(Impl, float) +DECLARE_GET_FROM_JSON(XGateImpl) +DECLARE_GET_FROM_JSON(YGateImpl) +DECLARE_GET_FROM_JSON(ZGateImpl) +DECLARE_GET_FROM_JSON(HGateImpl) +DECLARE_GET_FROM_JSON(SGateImpl) +DECLARE_GET_FROM_JSON(SdagGateImpl) +DECLARE_GET_FROM_JSON(TGateImpl) +DECLARE_GET_FROM_JSON(TdagGateImpl) +DECLARE_GET_FROM_JSON(SqrtXGateImpl) +DECLARE_GET_FROM_JSON(SqrtXdagGateImpl) +DECLARE_GET_FROM_JSON(SqrtYGateImpl) +DECLARE_GET_FROM_JSON(SqrtYdagGateImpl) +DECLARE_GET_FROM_JSON(P0GateImpl) +DECLARE_GET_FROM_JSON(P1GateImpl) +#undef DECLARE_GET_FROM_JSON +#undef DECLARE_GET_FROM_JSON_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_RGATE_WITH_TYPE(Impl, Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + Type angle = j.at("angle").get(); \ + return std::make_shared>( \ + vector_to_mask(targets), vector_to_mask(controls), angle); \ + } +#define DECLARE_GET_FROM_JSON_EACH_RGATE_WITH_TYPE(Type) \ + DECLARE_GET_FROM_JSON_RGATE_WITH_TYPE(RXGateImpl, Type) \ + DECLARE_GET_FROM_JSON_RGATE_WITH_TYPE(RYGateImpl, Type) \ + DECLARE_GET_FROM_JSON_RGATE_WITH_TYPE(RZGateImpl, Type) +DECLARE_GET_FROM_JSON_EACH_RGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_EACH_RGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_RGATE_WITH_TYPE +#undef DECLARE_GET_FROM_JSON_EACH_RGATE_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_UGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + Type theta = j.at("theta").get(); \ + return std::make_shared>( \ + vector_to_mask(targets), vector_to_mask(controls), theta); \ + } \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + Type theta = j.at("theta").get(); \ + Type phi = j.at("phi").get(); \ + return std::make_shared>( \ + vector_to_mask(targets), vector_to_mask(controls), theta, phi); \ + } \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + Type theta = j.at("theta").get(); \ + Type phi = j.at("phi").get(); \ + Type lambda = j.at("lambda").get(); \ + return std::make_shared>( \ + vector_to_mask(targets), vector_to_mask(controls), theta, phi, lambda); \ + } +DECLARE_GET_FROM_JSON_UGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_UGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_UGATE_WITH_TYPE + +#define DECLARE_GET_FROM_JSON_SWAPGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + return std::make_shared>(vector_to_mask(targets), \ + vector_to_mask(controls)); \ + } +DECLARE_GET_FROM_JSON_SWAPGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_SWAPGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_SWAPGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/param_gate.hpp b/include/scaluq/gate/param_gate.hpp index daf21a3f..cbdacb73 100644 --- a/include/scaluq/gate/param_gate.hpp +++ b/include/scaluq/gate/param_gate.hpp @@ -96,11 +96,16 @@ class ParamGateBase { std::vector params) const = 0; [[nodiscard]] virtual std::string to_string(const std::string& indent = "") const = 0; + + virtual void get_as_json(Json& j) const { j = Json{{"type", "Unknown"}}; } }; template concept ParamGateImpl = std::derived_from>; +template +inline std::shared_ptr get_from_json(const Json&); + template class ParamGatePtr { friend class ParamGateFactory; @@ -113,7 +118,7 @@ class ParamGatePtr { ParamGateType _param_gate_type; public: - ParamGatePtr() : _param_gate_ptr(nullptr), _param_gate_type(get_param_gate_type()) {} + ParamGatePtr() : _param_gate_ptr(nullptr), _param_gate_type(get_param_gate_type()) {} template ParamGatePtr(const std::shared_ptr& param_gate_ptr) { if constexpr (std::is_same_v) { @@ -163,6 +168,20 @@ class ParamGatePtr { os << gate->to_string(); return os; } + + friend void to_json(Json& j, const ParamGatePtr& gate) { gate->get_as_json(j); } + + friend void from_json(const Json& j, ParamGatePtr& gate) { + std::string type = j.at("type"); + + // clang-format off + if (type == "ParamRX") gate = get_from_json>(j); + else if (type == "ParamRY") gate = get_from_json>(j); + else if (type == "ParamRZ") gate = get_from_json>(j); + else if (type == "ParamPauliRotation") gate = get_from_json>(j); + else if (type == "ParamProbablistic") gate = get_from_json>(j); + // clang-format on + } }; } // namespace internal @@ -227,7 +246,25 @@ namespace internal { [](const PARAM_GATE_TYPE& gate, FLOAT param) { \ return gate->get_matrix(param); \ }, \ - "Get matrix representation of the gate with holding the parameter.") + "Get matrix representation of the gate with holding the parameter.") \ + .def( \ + "to_string", \ + [](const PARAM_GATE_TYPE& gate) { return gate->to_string(""); }, \ + "Get string representation of the gate.") \ + .def( \ + "__str__", \ + [](const PARAM_GATE_TYPE& gate) { return gate->to_string(""); }, \ + "Get string representation of the gate.") \ + .def( \ + "to_json", \ + [](const PARAM_GATE_TYPE& gate) { return Json(gate).dump(); }, \ + "Get JSON representation of the gate.") \ + .def( \ + "load_json", \ + [](PARAM_GATE_TYPE& gate, const std::string& str) { \ + gate = nlohmann::json::parse(str); \ + }, \ + "Read an object from the JSON representation of the gate.") template nb::class_> param_gate_base_def; diff --git a/include/scaluq/gate/param_gate_pauli.hpp b/include/scaluq/gate/param_gate_pauli.hpp index f58fdb4b..ee59261e 100644 --- a/include/scaluq/gate/param_gate_pauli.hpp +++ b/include/scaluq/gate/param_gate_pauli.hpp @@ -32,12 +32,36 @@ class ParamPauliRotationGateImpl : public ParamGateBase { void update_quantum_state(StateVectorBatched& states, std::vector params) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "ParamPauliRotation"}, + {"control", this->control_qubit_list()}, + {"pauli", this->pauli()}, + {"param_coef", this->param_coef()}}; + } }; } // namespace internal template using ParamPauliRotationGate = internal::ParamGatePtr>; +namespace internal { +#define DECLARE_GET_FROM_JSON_PARAM_PAULIGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto controls = j.at("control").get>(); \ + auto pauli = j.at("pauli").get>(); \ + auto param_coef = j.at("param_coef").get(); \ + return std::make_shared>( \ + vector_to_mask(controls), pauli, param_coef); \ + } + +DECLARE_GET_FROM_JSON_PARAM_PAULIGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_PARAM_PAULIGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_PARAM_PAULIGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/param_gate_probablistic.hpp b/include/scaluq/gate/param_gate_probablistic.hpp index d1dd5c93..035f4be1 100644 --- a/include/scaluq/gate/param_gate_probablistic.hpp +++ b/include/scaluq/gate/param_gate_probablistic.hpp @@ -3,8 +3,9 @@ #include #include "../util/random.hpp" -#include "gate.hpp" -#include "param_gate.hpp" +#include "gate_probablistic.hpp" +#include "param_gate_pauli.hpp" +#include "param_gate_standard.hpp" namespace scaluq { namespace internal { @@ -64,12 +65,44 @@ class ParamProbablisticGateImpl : public ParamGateBase { std::vector params) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "ParamProbablistic"}, + {"gate_list", Json::array()}, + {"distribution", this->distribution()}}; + + for (const auto& gate : this->gate_list()) { + std::visit([&](auto&& arg) { j["gate_list"].push_back(arg); }, gate); + } + } }; } // namespace internal template using ParamProbablisticGate = internal::ParamGatePtr>; +namespace internal { + +#define DECLARE_GET_FROM_JSON_PARAM_PROBGATE_WITH_TYPE(Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto distribution = j.at("distribution").get>(); \ + std::vector, ParamGate>> gate_list; \ + const Json& tmp_list = j.at("gate_list"); \ + for (const Json& tmp_j : tmp_list) { \ + if (tmp_j.at("type").get().starts_with("Param")) \ + gate_list.emplace_back(tmp_j.get>()); \ + else \ + gate_list.emplace_back(tmp_j.get>()); \ + } \ + return std::make_shared>(distribution, gate_list); \ + } +DECLARE_GET_FROM_JSON_PARAM_PROBGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_PARAM_PROBGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_PARAM_PROBGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/gate/param_gate_standard.hpp b/include/scaluq/gate/param_gate_standard.hpp index 68869c76..4ac64da2 100644 --- a/include/scaluq/gate/param_gate_standard.hpp +++ b/include/scaluq/gate/param_gate_standard.hpp @@ -23,6 +23,13 @@ class ParamRXGateImpl : public ParamGateBase { std::vector params) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "ParamRX"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"param_coef", this->param_coef()}}; + } }; template @@ -41,6 +48,13 @@ class ParamRYGateImpl : public ParamGateBase { std::vector params) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "ParamRY"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"param_coef", this->param_coef()}}; + } }; template @@ -59,6 +73,13 @@ class ParamRZGateImpl : public ParamGateBase { std::vector params) const override; std::string to_string(const std::string& indent) const override; + + void get_as_json(Json& j) const override { + j = Json{{"type", "ParamRZ"}, + {"target", this->target_qubit_list()}, + {"control", this->control_qubit_list()}, + {"param_coef", this->param_coef()}}; + } }; } // namespace internal @@ -70,6 +91,30 @@ using ParamRYGate = internal::ParamGatePtr>; template using ParamRZGate = internal::ParamGatePtr>; +namespace internal { + +#define DECLARE_GET_FROM_JSON_PARAM_RGATE_WITH_TYPE(Impl, Type) \ + template <> \ + inline std::shared_ptr> get_from_json(const Json& j) { \ + auto targets = j.at("target").get>(); \ + auto controls = j.at("control").get>(); \ + auto param_coef = j.at("param_coef").get(); \ + return std::make_shared>( \ + vector_to_mask(targets), vector_to_mask(controls), param_coef); \ + } + +#define DECLARE_GET_FROM_JSON_EACH_PARAM_RGATE_WITH_TYPE(Type) \ + DECLARE_GET_FROM_JSON_PARAM_RGATE_WITH_TYPE(ParamRXGateImpl, Type) \ + DECLARE_GET_FROM_JSON_PARAM_RGATE_WITH_TYPE(ParamRYGateImpl, Type) \ + DECLARE_GET_FROM_JSON_PARAM_RGATE_WITH_TYPE(ParamRZGateImpl, Type) + +DECLARE_GET_FROM_JSON_EACH_PARAM_RGATE_WITH_TYPE(double) +DECLARE_GET_FROM_JSON_EACH_PARAM_RGATE_WITH_TYPE(float) +#undef DECLARE_GET_FROM_JSON_PARAM_RGATE_WITH_TYPE +#undef DECLARE_GET_FROM_JSON_EACH_PARAM_RGATE_WITH_TYPE + +} // namespace internal + #ifdef SCALUQ_USE_NANOBIND namespace internal { template diff --git a/include/scaluq/operator/operator.hpp b/include/scaluq/operator/operator.hpp index a26630c6..b4e136ea 100644 --- a/include/scaluq/operator/operator.hpp +++ b/include/scaluq/operator/operator.hpp @@ -12,10 +12,11 @@ namespace scaluq { template class Operator { public: + Operator() = default; // for enable operator= from json explicit Operator(std::uint64_t n_qubits) : _n_qubits(n_qubits) {} - [[nodiscard]] inline bool is_hermitian() { return _is_hermitian; } - [[nodiscard]] inline std::uint64_t n_qubits() { return _n_qubits; } + [[nodiscard]] inline bool is_hermitian() const { return _is_hermitian; } + [[nodiscard]] inline std::uint64_t n_qubits() const { return _n_qubits; } [[nodiscard]] inline const std::vector>& terms() const { return _terms; } [[nodiscard]] std::string to_string() const; @@ -63,6 +64,24 @@ class Operator { Operator& operator*=(const PauliOperator& pauli); Operator operator*(const PauliOperator& pauli) const { return Operator(*this) *= pauli; } + friend void to_json(Json& j, const Operator& op) { + j = Json{{"n_qubits", op.n_qubits()}, {"terms", Json::array()}}; + for (const auto& pauli : op.terms()) { + Json tmp = pauli; + j["terms"].push_back(tmp); + } + } + friend void from_json(const Json& j, Operator& op) { + std::uint32_t n = j.at("n_qubits").get(); + Operator res(n); + for (const auto& term : j.at("terms")) { + std::string pauli_string = term.at("pauli_string").get(); + Kokkos::complex coef = term.at("coef").get>(); + res.add_operator({pauli_string, coef}); + } + op = res; + } + private: std::vector> _terms; std::uint64_t _n_qubits; @@ -124,7 +143,15 @@ void bind_operator_operator_hpp(nb::module_& m) { .def(nb::self -= PauliOperator()) .def(nb::self - PauliOperator()) .def(nb::self *= PauliOperator()) - .def(nb::self * PauliOperator()); + .def(nb::self * PauliOperator()) + .def( + "to_json", + [](const Operator& op) { return Json(op).dump(); }, + "Information as json style.") + .def( + "load_json", + [](Operator& op, const std::string& str) { op = nlohmann::json::parse(str); }, + "Read an object from the JSON representation of the operator."); } } // namespace internal #endif diff --git a/include/scaluq/operator/pauli_operator.hpp b/include/scaluq/operator/pauli_operator.hpp index 63b38287..a4fc245e 100644 --- a/include/scaluq/operator/pauli_operator.hpp +++ b/include/scaluq/operator/pauli_operator.hpp @@ -96,6 +96,14 @@ class PauliOperator { [[nodiscard]] inline PauliOperator operator*(Complex target) const { return PauliOperator(_ptr->_target_qubit_list, _ptr->_pauli_id_list, _ptr->_coef * target); } + + friend void to_json(Json& j, const PauliOperator& pauli) { + j = Json{{"pauli_string", pauli.get_pauli_string()}, {"coef", pauli.coef()}}; + } + friend void from_json(const Json& j, PauliOperator& pauli) { + pauli = PauliOperator(j.at("pauli_string").get(), + j.at("coef").get>()); + } }; #ifdef SCALUQ_USE_NANOBIND @@ -219,7 +227,17 @@ void bind_operator_pauli_operator_hpp(nb::module_& m) { &PauliOperator::get_transition_amplitude, "Get transition amplitude of measuring state vector. $\\bra{\\chi}P\\ket{\\psi}$.") .def(nb::self * nb::self) - .def(nb::self * Complex()); + .def(nb::self * Complex()) + .def( + "to_json", + [](const PauliOperator& pauli) { return Json(pauli).dump(); }, + "Information as json style.") + .def( + "load_json", + [](PauliOperator& pauli, const std::string& str) { + pauli = nlohmann::json::parse(str); + }, + "Read an object from the JSON representation of the Pauli operator."); } } // namespace internal #endif diff --git a/include/scaluq/state/state_vector.hpp b/include/scaluq/state/state_vector.hpp index 1acd96bf..a3d8292e 100644 --- a/include/scaluq/state/state_vector.hpp +++ b/include/scaluq/state/state_vector.hpp @@ -85,6 +85,14 @@ class StateVector { } [[nodiscard]] std::string to_string() const; + + friend void to_json(Json& j, const StateVector& state) { + j = Json{{"n_qubits", state._n_qubits}, {"amplitudes", state.get_amplitudes()}}; + } + friend void from_json(const Json& j, StateVector& state) { + state = StateVector(j.at("n_qubits").get()); + state.load(j.at("amplitudes").get>()); + } }; #ifdef SCALUQ_USE_NANOBIND @@ -493,6 +501,27 @@ void bind_state_state_vector_hpp(nb::module_& m) { .desc("Constant used for `StateVector::get_marginal_probability` to express the " "the qubit is not measured.") .build_as_google_style() + .c_str()) + .def( + "to_json", + [](const StateVector& state) { return Json(state).dump(); }, + DocString() + .desc("Information as json style.") + .ret("str", "information as json style") + .ex(DocString::Code{ + ">>> state = StateVector(1)", + ">>> state.to_json()", + R"('{"amplitudes":[{"imag":0.0,"real":1.0},{"imag":0.0,"real":0.0}],"n_qubits":1}')"}) + .build_as_google_style() + .c_str()) + .def( + "load_json", + [](StateVector& state, const std::string& str) { + state = nlohmann::json::parse(str); + }, + DocString() + .desc("Read an object from the JSON representation of the state vector.") + .build_as_google_style() .c_str()); } } // namespace internal diff --git a/include/scaluq/state/state_vector_batched.hpp b/include/scaluq/state/state_vector_batched.hpp index d081d925..d5a34b02 100644 --- a/include/scaluq/state/state_vector_batched.hpp +++ b/include/scaluq/state/state_vector_batched.hpp @@ -82,6 +82,38 @@ class StateVectorBatched { os << states.to_string(); return os; } + + friend void to_json(Json& j, const StateVectorBatched& states) { + auto amplitudes = states.get_amplitudes(); + + j = Json{{"n_qubits", states._n_qubits}, + {"batch_size", states._batch_size}, + {"batched_amplitudes", Json::array()}}; + for (std::uint32_t i = 0; i < amplitudes.size(); ++i) { + Json state = {{"amplitudes", Json::array()}}; + for (const auto& amp : amplitudes[i]) { + state["amplitudes"].push_back({{"real", amp.real()}, {"imag", amp.imag()}}); + } + j["batched_amplitudes"].push_back(state); + } + } + friend void from_json(const Json& j, StateVectorBatched& states) { + std::uint32_t b = j.at("batch_size").get(); + std::uint32_t n = j.at("n_qubits").get(); + states = StateVectorBatched(b, n); + + const auto& batched_amplitudes = j.at("batched_amplitudes"); + std::vector res(b, std::vector>(1ULL << n)); + for (std::uint32_t i = 0; i < b; ++i) { + const auto& amplitudes = batched_amplitudes[i].at("amplitudes"); + for (std::uint32_t j = 0; j < (1ULL << n); ++j) { + Fp real = amplitudes[j].at("real").get(); + Fp imag = amplitudes[j].at("imag").get(); + res[i][j] = Kokkos::complex(real, imag); + } + } + states.load(res); + } }; #ifdef SCALUQ_USE_NANOBIND @@ -180,7 +212,17 @@ void bind_state_state_vector_batched_hpp(nb::module_& m) { "Load batched amplitudes from `list[list[complex]]`.") .def("copy", &StateVectorBatched::copy, "Create a copy of the batched state vector.") .def("to_string", &StateVectorBatched::to_string, "Information as `str`.") - .def("__str__", &StateVectorBatched::to_string, "Information as `str`."); + .def("__str__", &StateVectorBatched::to_string, "Information as `str`.") + .def( + "to_json", + [](const StateVectorBatched& states) { return Json(states).dump(); }, + "Get JSON representation of the states.") + .def( + "load_json", + [](StateVectorBatched& states, const std::string& str) { + states = nlohmann::json::parse(str); + }, + "Read an object from the JSON representation of the states."); } } // namespace internal #endif diff --git a/include/scaluq/types.hpp b/include/scaluq/types.hpp index d3566acf..223a12cb 100644 --- a/include/scaluq/types.hpp +++ b/include/scaluq/types.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "kokkos.hpp" @@ -13,6 +14,7 @@ template using StdComplex = std::complex; template using Complex = Kokkos::complex; +using Json = nlohmann::json; namespace internal { template @@ -49,3 +51,16 @@ class SparseMatrix { }; } // namespace internal } // namespace scaluq + +namespace nlohmann { +template +struct adl_serializer> { + static void to_json(json& j, const scaluq::Complex& c) { + j = json{{"real", c.real()}, {"imag", c.imag()}}; + } + static void from_json(const json& j, scaluq::Complex& c) { + j.at("real").get_to(c.real()); + j.at("imag").get_to(c.imag()); + } +}; +} // namespace nlohmann diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 40d91efd..57c9e815 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 3.21) - message(STATUS "Building library for python...") nanobind_add_module(scaluq_core STABLE_ABI binding.cpp) target_link_libraries(scaluq_core PRIVATE scaluq Kokkos::kokkos + nlohmann_json::nlohmann_json ) target_include_directories(scaluq_core PRIVATE ${PROJECT_SOURCE_DIR}/include) target_include_directories(scaluq_core PRIVATE ${eigen_SOURCE_DIR}) diff --git a/python/binding.cpp b/python/binding.cpp index c9fb21c6..d9059a10 100644 --- a/python/binding.cpp +++ b/python/binding.cpp @@ -83,9 +83,9 @@ void cleanup() { template void bind_on_precision(nb::module_& m, const char* submodule_name) { - auto mp = m.def_submodule( - submodule_name, - (std::ostringstream("module for ") << submodule_name << "precision").str().c_str()); + std::ostringstream oss; + oss << "module for " << submodule_name << "precision"; + auto mp = m.def_submodule(submodule_name, oss.str().c_str()); internal::bind_state_state_vector_hpp(mp); internal::bind_state_state_vector_batched_hpp(mp); diff --git a/script/build_gcc.sh b/script/build_gcc.sh index 0053e34a..2272853e 100755 --- a/script/build_gcc.sh +++ b/script/build_gcc.sh @@ -2,7 +2,7 @@ set -eux -script/configure +script/configure_dev if [ "$(uname)" = 'Darwin' ]; then NPROC=$(sysctl -n hw.logicalcpu) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93c08da7..705578ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,10 +27,11 @@ target_sources(scaluq PRIVATE ) target_link_libraries(scaluq PUBLIC Kokkos::kokkos + nlohmann_json::nlohmann_json ) target_include_directories(scaluq PRIVATE $ $) target_include_directories(scaluq PRIVATE ${eigen_SOURCE_DIR}) install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/scaluq DESTINATION include/scaluq) -install(TARGETS scaluq kokkos kokkoscore kokkoscontainers kokkosalgorithms kokkossimd LIBDL EXPORT scaluqTargets LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include) +install(TARGETS scaluq kokkos kokkoscore kokkoscontainers kokkosalgorithms kokkossimd nlohmann_json LIBDL EXPORT scaluqTargets LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include) install(EXPORT scaluqTargets FILE scaluq-config.cmake DESTINATION share/cmake/scaluq/ NAMESPACE scaluq::) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9d15ca2d..b91dd1a6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,6 +20,8 @@ target_link_libraries(scaluq_test PUBLIC scaluq Kokkos::kokkos GTest::gtest_main + Eigen3::Eigen + nlohmann_json::nlohmann_json ) target_include_directories(scaluq_test PRIVATE $ $) target_include_directories(scaluq_test PRIVATE ${eigen_SOURCE_DIR})