From 9aad61bf88f171bebc64e51aec744ecc78b82894 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Fri, 22 Nov 2024 11:45:13 +0900 Subject: [PATCH] python precision --- include/scaluq/circuit/circuit.hpp | 43 +- include/scaluq/gate/gate.hpp | 37 +- include/scaluq/gate/gate_factory.hpp | 73 ++-- include/scaluq/gate/gate_matrix.hpp | 13 +- include/scaluq/gate/gate_pauli.hpp | 5 +- include/scaluq/gate/gate_probablistic.hpp | 7 +- include/scaluq/gate/param_gate.hpp | 40 +- include/scaluq/gate/param_gate_factory.hpp | 19 +- include/scaluq/gate/param_gate_pauli.hpp | 3 +- .../scaluq/gate/param_gate_probablistic.hpp | 7 +- include/scaluq/gate/param_gate_standard.hpp | 7 +- include/scaluq/kokkos.hpp | 16 + include/scaluq/operator/operator.hpp | 49 +-- include/scaluq/operator/pauli_operator.hpp | 77 ++-- include/scaluq/state/state_vector.hpp | 370 ++++++++++++++++-- include/scaluq/types.hpp | 17 - python/CMakeLists.txt | 29 +- python/binding.cpp | 55 +-- python/scaluq/__init__.py | 2 + python/scaluq/f32/__init__.py | 2 + python/scaluq/f32/gate/__init__.py | 1 + python/scaluq/f64/__init__.py | 2 + python/scaluq/f64/gate/__init__.py | 1 + python/scaluq/gate.py | 1 - 24 files changed, 583 insertions(+), 293 deletions(-) create mode 100644 python/scaluq/f32/__init__.py create mode 100644 python/scaluq/f32/gate/__init__.py create mode 100644 python/scaluq/f64/__init__.py create mode 100644 python/scaluq/f64/gate/__init__.py delete mode 100644 python/scaluq/gate.py diff --git a/include/scaluq/circuit/circuit.hpp b/include/scaluq/circuit/circuit.hpp index cbf78efb..1aa1614e 100644 --- a/include/scaluq/circuit/circuit.hpp +++ b/include/scaluq/circuit/circuit.hpp @@ -76,42 +76,43 @@ class Circuit { #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_circuit_circuit_hpp(nb::module_& m) { - nb::class_>(m, "Circuit", "Quantum circuit represented as gate array") + nb::class_>(m, "Circuit", "Quantum circuit represented as gate array") .def(nb::init(), "Initialize empty circuit of specified qubits.") - .def("n_qubits", &Circuit::n_qubits, "Get property of `n_qubits`.") + .def("n_qubits", &Circuit::n_qubits, "Get property of `n_qubits`.") .def("gate_list", - &Circuit::gate_list, + &Circuit::gate_list, "Get property of `gate_list`.", nb::rv_policy::reference) - .def("n_gates", &Circuit::n_gates, "Get property of `n_gates`.") - .def("key_set", &Circuit::key_set, "Get set of keys of parameters.") - .def("get_gate_at", &Circuit::get_gate_at, "Get reference of i-th gate.") + .def("n_gates", &Circuit::n_gates, "Get property of `n_gates`.") + .def("key_set", &Circuit::key_set, "Get set of keys of parameters.") + .def("get_gate_at", &Circuit::get_gate_at, "Get reference of i-th gate.") .def("get_param_key_at", - &Circuit::get_param_key_at, + &Circuit::get_param_key_at, "Get parameter key of i-th gate. If it is not parametric, return None.") - .def("calculate_depth", &Circuit::calculate_depth, "Get depth of circuit.") + .def("calculate_depth", &Circuit::calculate_depth, "Get depth of circuit.") .def("add_gate", - nb::overload_cast&>(&Circuit::add_gate), + nb::overload_cast&>(&Circuit::add_gate), "Add gate. Given gate is copied.") - .def("add_param_gate", - nb::overload_cast&, std::string_view>( - &Circuit::add_param_gate), - "Add parametric gate with specifing key. Given param_gate is copied.") + .def( + "add_param_gate", + nb::overload_cast&, std::string_view>(&Circuit::add_param_gate), + "Add parametric gate with specifing key. Given param_gate is copied.") .def("add_circuit", - nb::overload_cast&>(&Circuit::add_circuit), + nb::overload_cast&>(&Circuit::add_circuit), "Add all gates in specified circuit. Given gates are copied.") .def("update_quantum_state", - &Circuit::update_quantum_state, + &Circuit::update_quantum_state, "Apply gate to the StateVector. StateVector in args is directly updated. If the " "circuit contains parametric gate, you have to give real value of parameter as " "dict[str, float] in 2nd arg.") .def( "update_quantum_state", - [&](const Circuit& circuit, StateVector& state, nb::kwargs kwargs) { - std::map parameters; + [&](const Circuit& circuit, StateVector& state, nb::kwargs kwargs) { + std::map parameters; for (auto&& [key, param] : kwargs) { - parameters[nb::cast(key)] = nb::cast(param); + parameters[nb::cast(key)] = nb::cast(param); } circuit.update_quantum_state(state, parameters); }, @@ -119,12 +120,12 @@ void bind_circuit_circuit_hpp(nb::module_& m) { "circuit contains parametric gate, you have to give real value of parameter as " "\"name=value\" format in kwargs.") .def("update_quantum_state", - [](const Circuit& circuit, StateVector& state) { + [](const Circuit& circuit, StateVector& state) { circuit.update_quantum_state(state); }) - .def("copy", &Circuit::copy, "Copy circuit. All the gates inside is copied.") + .def("copy", &Circuit::copy, "Copy circuit. All the gates inside is copied.") .def("get_inverse", - &Circuit::get_inverse, + &Circuit::get_inverse, "Get inverse of circuit. All the gates are newly created."); } } // namespace internal diff --git a/include/scaluq/gate/gate.hpp b/include/scaluq/gate/gate.hpp index 8b163243..b5a321d8 100644 --- a/include/scaluq/gate/gate.hpp +++ b/include/scaluq/gate/gate.hpp @@ -342,12 +342,12 @@ namespace internal { [](const GATE_TYPE& gate) { return gate->to_string(""); }, \ "Get string representation of the gate.") -nb::class_> gate_base_def_double; -nb::class_> gate_base_def_float; +template +nb::class_> gate_base_def; #define DEF_GATE(GATE_TYPE, FLOAT, DESCRIPTION) \ - ::scaluq::internal::gate_base_def_##FLOAT.def(nb::init>(), \ - "Upcast from `" #GATE_TYPE "`."); \ + ::scaluq::internal::gate_base_def.def(nb::init>(), \ + "Upcast from `" #GATE_TYPE "`."); \ DEF_GATE_BASE( \ GATE_TYPE, \ FLOAT, \ @@ -355,8 +355,7 @@ nb::class_> gate_base_def_float; "\n\n.. note:: Upcast is required to use gate-general functions (ex: add to Circuit).") \ .def(nb::init>()) -template -void bind_gate_gate_hpp(nb::module_& m) { +void bind_gate_gate_hpp_without_precision(nb::module_& m) { nb::enum_(m, "GateType", "Enum of Gate Type.") .value("I", GateType::I) .value("GlobalPhase", GateType::GlobalPhase) @@ -385,24 +384,16 @@ void bind_gate_gate_hpp(nb::module_& m) { .value("TwoTargetMatrix", GateType::TwoTargetMatrix) .value("Pauli", GateType::Pauli) .value("PauliRotation", GateType::PauliRotation); +} - if constexpr (std::is_same_v) { - gate_base_def_double = - DEF_GATE_BASE(Gate, - double, - "General class of QuantumGate.\n\n.. note:: Downcast to requred to use " - "gate-specific functions.") - .def(nb::init>(), "Just copy shallowly."); - } else if constexpr (std::is_same_v) { - gate_base_def_float = - DEF_GATE_BASE(Gate, - float, - "General class of QuantumGate.\n\n.. note:: Downcast to requred to use " - "gate-specific functions.") - .def(nb::init>(), "Just copy shallowly."); - } else { - static_assert(internal::lazy_false_v); - } +template +void bind_gate_gate_hpp(nb::module_& m) { + gate_base_def = + DEF_GATE_BASE(Gate, + Fp, + "General class of QuantumGate.\n\n.. note:: Downcast to requred to use " + "gate-specific functions.") + .def(nb::init>(), "Just copy shallowly."); } } // namespace internal #endif diff --git a/include/scaluq/gate/gate_factory.hpp b/include/scaluq/gate/gate_factory.hpp index bf66d02a..9acf710e 100644 --- a/include/scaluq/gate/gate_factory.hpp +++ b/include/scaluq/gate/gate_factory.hpp @@ -251,116 +251,117 @@ inline Gate Probablistic(const std::vector& distribution, #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_gate_factory_hpp(nb::module_& mgate) { - mgate.def("I", &gate::I, "Generate general Gate class instance of I."); + mgate.def("I", &gate::I, "Generate general Gate class instance of I."); mgate.def("GlobalPhase", - &gate::GlobalPhase, + &gate::GlobalPhase, "Generate general Gate class instance of GlobalPhase.", "phase"_a, "controls"_a = std::vector{}); mgate.def("X", - &gate::X, + &gate::X, "Generate general Gate class instance of X.", "target"_a, "controls"_a = std::vector{}); mgate.def("Y", - &gate::Y, + &gate::Y, "Generate general Gate class instance of Y.", "taget"_a, "controls"_a = std::vector{}); mgate.def("Z", - &gate::Z, + &gate::Z, "Generate general Gate class instance of Z.", "target"_a, "controls"_a = std::vector{}); mgate.def("H", - &gate::H, + &gate::H, "Generate general Gate class instance of H.", "target"_a, "controls"_a = std::vector{}); mgate.def("S", - &gate::S, + &gate::S, "Generate general Gate class instance of S.", "target"_a, "controls"_a = std::vector{}); mgate.def("Sdag", - &gate::Sdag, + &gate::Sdag, "Generate general Gate class instance of Sdag.", "target"_a, "controls"_a = std::vector{}); mgate.def("T", - &gate::T, + &gate::T, "Generate general Gate class instance of T.", "target"_a, "controls"_a = std::vector{}); mgate.def("Tdag", - &gate::Tdag, + &gate::Tdag, "Generate general Gate class instance of Tdag.", "target"_a, "controls"_a = std::vector{}); mgate.def("SqrtX", - &gate::SqrtX, + &gate::SqrtX, "Generate general Gate class instance of SqrtX.", "target"_a, "controls"_a = std::vector{}); mgate.def("SqrtXdag", - &gate::SqrtXdag, + &gate::SqrtXdag, "Generate general Gate class instance of SqrtXdag.", "target"_a, "controls"_a = std::vector{}); mgate.def("SqrtY", - &gate::SqrtY, + &gate::SqrtY, "Generate general Gate class instance of SqrtY.", "target"_a, "controls"_a = std::vector{}); mgate.def("SqrtYdag", - &gate::SqrtYdag, + &gate::SqrtYdag, "Generate general Gate class instance of SqrtYdag.", "target"_a, "controls"_a = std::vector{}); mgate.def("P0", - &gate::P0, + &gate::P0, "Generate general Gate class instance of P0.", "target"_a, "controls"_a = std::vector{}); mgate.def("P1", - &gate::P1, + &gate::P1, "Generate general Gate class instance of P1.", "target"_a, "controls"_a = std::vector{}); mgate.def("RX", - &gate::RX, + &gate::RX, "Generate general Gate class instance of RX.", "target"_a, "angle"_a, "controls"_a = std::vector{}); mgate.def("RY", - &gate::RY, + &gate::RY, "Generate general Gate class instance of RY.", "target"_a, "angle"_a, "controls"_a = std::vector{}); mgate.def("RZ", - &gate::RZ, + &gate::RZ, "Generate general Gate class instance of RZ.", "target"_a, "angle"_a, "controls"_a = std::vector{}); mgate.def("U1", - &gate::U1, + &gate::U1, "Generate general Gate class instance of U1.", "target"_a, "lambda_"_a, "controls"_a = std::vector{}); mgate.def("U2", - &gate::U2, + &gate::U2, "Generate general Gate class instance of U2.", "target"_a, "phi"_a, "lambda_"_a, "controls"_a = std::vector{}); mgate.def("U3", - &gate::U3, + &gate::U3, "Generate general Gate class instance of U3.", "target"_a, "theta"_a, @@ -368,73 +369,73 @@ void bind_gate_gate_factory_hpp(nb::module_& mgate) { "lambda_"_a, "controls"_a = std::vector{}); mgate.def("Swap", - &gate::Swap, + &gate::Swap, "Generate general Gate class instance of Swap.", "target1"_a, "target2"_a, "controls"_a = std::vector{}); mgate.def( "CX", - &gate::CX, + &gate::CX, "Generate general Gate class instance of CX.\n\n.. note:: CX is a specialization of X."); mgate.def("CNot", - &gate::CX, + &gate::CX, "Generate general Gate class instance of CNot.\n\n.. note:: CNot is an alias of CX."); mgate.def( "CZ", - &gate::CZ, + &gate::CZ, "Generate general Gate class instance of CZ.\n\n.. note:: CZ is a specialization of Z."); mgate.def( "CCX", - &gate::CCX, + &gate::CCX, "Generate general Gate class instance of CXX.\n\n.. note:: CX is a specialization of X."); mgate.def( "CCNot", - &gate::CCX, + &gate::CCX, "Generate general Gate class instance of CCNot.\n\n.. note:: CCNot is an alias of CCX."); mgate.def("Toffoli", - &gate::CCX, + &gate::CCX, "Generate general Gate class instance of Toffoli.\n\n.. note:: Toffoli is an alias " "of CCX."); mgate.def("OneTargetMatrix", - &gate::OneTargetMatrix, + &gate::OneTargetMatrix, "Generate general Gate class instance of OneTargetMatrix.", "target"_a, "matrix"_a, "controls"_a = std::vector{}); mgate.def("TwoTargetMatrix", - &gate::TwoTargetMatrix, + &gate::TwoTargetMatrix, "Generate general Gate class instance of TwoTargetMatrix.", "target1"_a, "target2"_a, "matrix"_a, "controls"_a = std::vector{}); mgate.def("DenseMatrix", - &gate::DenseMatrix, + &gate::DenseMatrix, "Generate general Gate class instance of DenseMatrix.", "targets"_a, "matrix"_a, "controls"_a = std::vector{}, "is_unitary"_a = false); mgate.def("SparseMatrix", - &gate::SparseMatrix, + &gate::SparseMatrix, "Generate general Gate class instance of SparseMatrix.", "targets"_a, "matrix"_a, "controls"_a = std::vector{}); mgate.def("Pauli", - &gate::Pauli, + &gate::Pauli, "Generate general Gate class instance of Pauli.", "pauli"_a, "controls"_a = std::vector{}); mgate.def("PauliRotation", - &gate::PauliRotation, + &gate::PauliRotation, "Generate general Gate class instance of PauliRotation.", "pauli"_a, "angle"_a, "controls"_a = std::vector{}); mgate.def("Probablistic", - &gate::Probablistic, + &gate::Probablistic, "Generate general Gate class instance of Probablistic.", "distribution"_a, "gate_list"_a); diff --git a/include/scaluq/gate/gate_matrix.hpp b/include/scaluq/gate/gate_matrix.hpp index 54a87bdb..d3f0fa96 100644 --- a/include/scaluq/gate/gate_matrix.hpp +++ b/include/scaluq/gate/gate_matrix.hpp @@ -147,17 +147,18 @@ using DenseMatrixGate = internal::GatePtr>; #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_gate_matrix_hpp(nb::module_& m) { - DEF_GATE(OneTargetMatrixGate, double, "Specific class of one-qubit dense matrix gate.") + DEF_GATE(OneTargetMatrixGate, Fp, "Specific class of one-qubit dense matrix gate.") .def("matrix", [](const OneTargetMatrixGate& gate) { return gate->matrix(); }); - DEF_GATE(TwoTargetMatrixGate, double, "Specific class of two-qubit dense matrix gate.") + DEF_GATE(TwoTargetMatrixGate, Fp, "Specific class of two-qubit dense matrix gate.") .def("matrix", [](const TwoTargetMatrixGate& gate) { return gate->matrix(); }); - DEF_GATE(SparseMatrixGate, double, "Specific class of sparse matrix gate.") + DEF_GATE(SparseMatrixGate, Fp, "Specific class of sparse matrix gate.") .def("matrix", [](const SparseMatrixGate& gate) { return gate->get_matrix(); }) .def("sparse_matrix", - [](const SparseMatrixGate& gate) { return gate->get_sparse_matrix(); }); - DEF_GATE(DenseMatrixGate, double, "Specific class of dense matrix gate.") - .def("matrix", [](const DenseMatrixGate& gate) { return gate->get_matrix(); }); + [](const SparseMatrixGate& gate) { return gate->get_sparse_matrix(); }); + DEF_GATE(DenseMatrixGate, Fp, "Specific class of dense matrix gate.") + .def("matrix", [](const DenseMatrixGate& gate) { return gate->get_matrix(); }); } } // namespace internal #endif diff --git a/include/scaluq/gate/gate_pauli.hpp b/include/scaluq/gate/gate_pauli.hpp index 860e1b3f..8e088c92 100644 --- a/include/scaluq/gate/gate_pauli.hpp +++ b/include/scaluq/gate/gate_pauli.hpp @@ -66,14 +66,15 @@ using PauliRotationGate = internal::GatePtr> #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_gate_pauli_hpp(nb::module_& m) { DEF_GATE(PauliGate, - double, + Fp, "Specific class of multi-qubit pauli gate, which applies single-qubit Pauli " "gate to " "each of qubit."); DEF_GATE(PauliRotationGate, - double, + Fp, "Specific class of multi-qubit pauli-rotation gate, represented as " "$e^{-i\\frac{\\mathrm{angle}}{2}P}$."); } diff --git a/include/scaluq/gate/gate_probablistic.hpp b/include/scaluq/gate/gate_probablistic.hpp index 24d6be09..5072695b 100644 --- a/include/scaluq/gate/gate_probablistic.hpp +++ b/include/scaluq/gate/gate_probablistic.hpp @@ -67,18 +67,19 @@ using ProbablisticGate = internal::GatePtr>; #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_gate_probablistic(nb::module_& m) { DEF_GATE(ProbablisticGate, - double, + Fp, "Specific class of probablistic gate. The gate to apply is picked from a cirtain " "distribution.") .def( "gate_list", - [](const ProbablisticGate& gate) { return gate->gate_list(); }, + [](const ProbablisticGate& gate) { return gate->gate_list(); }, nb::rv_policy::reference) .def( "distribution", - [](const ProbablisticGate& gate) { return gate->distribution(); }, + [](const ProbablisticGate& gate) { return gate->distribution(); }, nb::rv_policy::reference); } } // namespace internal diff --git a/include/scaluq/gate/param_gate.hpp b/include/scaluq/gate/param_gate.hpp index 4a77968b..52aef2d3 100644 --- a/include/scaluq/gate/param_gate.hpp +++ b/include/scaluq/gate/param_gate.hpp @@ -218,12 +218,12 @@ namespace internal { }, \ "Get matrix representation of the gate with holding the parameter.") -nb::class_> param_gate_base_def_double; -nb::class_> param_gate_base_def_float; +template +nb::class_> param_gate_base_def; #define DEF_PARAM_GATE(PARAM_GATE_TYPE, FLOAT, DESCRIPTION) \ - ::scaluq::internal::param_gate_base_def_##FLOAT.def(nb::init>(), \ - "Upcast from `" #PARAM_GATE_TYPE "`."); \ + ::scaluq::internal::param_gate_base_def.def(nb::init>(), \ + "Upcast from `" #PARAM_GATE_TYPE "`."); \ DEF_PARAM_GATE_BASE( \ PARAM_GATE_TYPE, \ FLOAT, \ @@ -231,33 +231,23 @@ nb::class_> param_gate_base_def_float; "\n\n.. note:: Upcast is required to use gate-general functions (ex: add to Circuit).") \ .def(nb::init>()) -template -void bind_gate_param_gate_hpp(nb::module_& m) { +void bind_gate_param_gate_hpp_without_precision(nb::module_& m) { nb::enum_(m, "ParamGateType", "Enum of ParamGate Type.") .value("ParamRX", ParamGateType::ParamRX) .value("ParamRY", ParamGateType::ParamRY) .value("ParamRZ", ParamGateType::ParamRZ) .value("ParamPauliRotation", ParamGateType::ParamPauliRotation); +} - if constexpr (std::is_same_v) { - param_gate_base_def_double = - DEF_PARAM_GATE_BASE( - ParamGate, - double, - "General class of parametric quantum gate.\n\n.. note:: Downcast to requred to use " - "gate-specific functions.") - .def(nb::init>(), "Just copy shallowly."); - } else if constexpr (std::is_same_v) { - param_gate_base_def_float = - DEF_PARAM_GATE_BASE( - ParamGate, - float, - "General class of parametric quantum gate.\n\n.. note:: Downcast to requred to use " - "gate-specific functions.") - .def(nb::init>(), "Just copy shallowly."); - } else { - static_asert(internal::lazy_false_v); - } +template +void bind_gate_param_gate_hpp(nb::module_& m) { + param_gate_base_def = + DEF_PARAM_GATE_BASE( + ParamGate, + Fp, + "General class of parametric quantum gate.\n\n.. note:: Downcast to requred to use " + "gate-specific functions.") + .def(nb::init>(), "Just copy shallowly."); } } // namespace internal diff --git a/include/scaluq/gate/param_gate_factory.hpp b/include/scaluq/gate/param_gate_factory.hpp index 781ca0ff..1031d616 100644 --- a/include/scaluq/gate/param_gate_factory.hpp +++ b/include/scaluq/gate/param_gate_factory.hpp @@ -54,47 +54,48 @@ inline ParamGate ParamProbablistic( #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_param_gate_factory(nb::module_& mgate) { mgate.def("ParamRX", - &gate::ParamRX, + &gate::ParamRX, "Generate general ParamGate class instance of ParamRX.", "target"_a, "coef"_a = 1., "controls"_a = std::vector{}); mgate.def("ParamRY", - &gate::ParamRY, + &gate::ParamRY, "Generate general ParamGate class instance of ParamRY.", "target"_a, "coef"_a = 1., "controls"_a = std::vector{}); mgate.def("ParamRZ", - &gate::ParamRZ, + &gate::ParamRZ, "Generate general ParamGate class instance of ParamRZ.", "target"_a, "coef"_a = 1., "controls"_a = std::vector{}); mgate.def("ParamPauliRotation", - &gate::ParamPauliRotation, + &gate::ParamPauliRotation, "Generate general ParamGate class instance of ParamPauliRotation.", "pauli"_a, "coef"_a = 1., "controls"_a = std::vector{}); mgate.def("ParamProbablistic", - &gate::ParamProbablistic, + &gate::ParamProbablistic, "Generate general ParamGate class instance of ParamProbablistic."); mgate.def( "ParamProbablistic", - [](const std::vector, ParamGate>>>& + [](const std::vector, ParamGate>>>& prob_gate_list) { - std::vector distribution; - std::vector, ParamGate>> gate_list; + std::vector distribution; + std::vector, ParamGate>> gate_list; distribution.reserve(prob_gate_list.size()); gate_list.reserve(prob_gate_list.size()); for (const auto& [prob, gate] : prob_gate_list) { distribution.push_back(prob); gate_list.push_back(gate); } - return gate::ParamProbablistic(distribution, gate_list); + return gate::ParamProbablistic(distribution, gate_list); }, "Generate general ParamGate class instance of ParamProbablistic."); } diff --git a/include/scaluq/gate/param_gate_pauli.hpp b/include/scaluq/gate/param_gate_pauli.hpp index 422a2500..bb714aae 100644 --- a/include/scaluq/gate/param_gate_pauli.hpp +++ b/include/scaluq/gate/param_gate_pauli.hpp @@ -38,10 +38,11 @@ using ParamPauliRotationGate = internal::ParamGatePtr void bind_gate_param_gate_pauli_hpp(nb::module_& m) { DEF_PARAM_GATE( ParamPauliRotationGate, - double, + Fp, "Specific class of parametric multi-qubit pauli-rotation gate, represented as " "$e^{-i\\frac{\\mathrm{angle}}{2}P}$. `angle` is given as `param * param_coef`."); } diff --git a/include/scaluq/gate/param_gate_probablistic.hpp b/include/scaluq/gate/param_gate_probablistic.hpp index 4800d2b2..fdec2be2 100644 --- a/include/scaluq/gate/param_gate_probablistic.hpp +++ b/include/scaluq/gate/param_gate_probablistic.hpp @@ -70,20 +70,21 @@ using ParamProbablisticGate = internal::ParamGatePtr void bind_gate_param_gate_probablistic_hpp(nb::module_& m) { DEF_PARAM_GATE( ParamProbablisticGate, - double, + Fp, "Specific class of parametric probablistic gate. The gate to apply is picked from a " "cirtain " "distribution.") .def( "gate_list", - [](const ParamProbablisticGate& gate) { return gate->gate_list(); }, + [](const ParamProbablisticGate& gate) { return gate->gate_list(); }, nb::rv_policy::reference) .def( "distribution", - [](const ParamProbablisticGate& gate) { return gate->distribution(); }, + [](const ParamProbablisticGate& gate) { return gate->distribution(); }, nb::rv_policy::reference); } } // namespace internal diff --git a/include/scaluq/gate/param_gate_standard.hpp b/include/scaluq/gate/param_gate_standard.hpp index f36a1b7b..f26acb9b 100644 --- a/include/scaluq/gate/param_gate_standard.hpp +++ b/include/scaluq/gate/param_gate_standard.hpp @@ -66,20 +66,21 @@ using ParamRZGate = internal::ParamGatePtr>; #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_gate_param_gate_standard_hpp(nb::module_& m) { DEF_PARAM_GATE( ParamRXGate, - double, + Fp, "Specific class of parametric X rotation gate, represented as " "$e^{-i\\frac{\\mathrm{angle}}{2}X}$. `angle` is given as `param * param_coef`."); DEF_PARAM_GATE( ParamRYGate, - double, + Fp, "Specific class of parametric Y rotation gate, represented as " "$e^{-i\\frac{\\mathrm{angle}}{2}Y}$. `angle` is given as `param * param_coef`."); DEF_PARAM_GATE( ParamRZGate, - double, + Fp, "Specific class of parametric Z rotation gate, represented as " "$e^{-i\\frac{\\mathrm{angle}}{2}Z}$. `angle` is given as `param * param_coef`."); } diff --git a/include/scaluq/kokkos.hpp b/include/scaluq/kokkos.hpp index 781dda41..7aa62b82 100644 --- a/include/scaluq/kokkos.hpp +++ b/include/scaluq/kokkos.hpp @@ -5,4 +5,20 @@ void initialize(); void finalize(); bool is_initialized(); bool is_finalized(); + +#ifdef SCALUQ_USE_NANOBIND +namespace internal { +void bind_kokkos_hpp(nb::module_& m) { + m.def("finalize", + &finalize, + "Terminate the Kokkos execution environment. Release the resources.\n\n.. note:: " + "Finalization fails if there exists `StateVector` allocated. You must use " + "`StateVector` only inside inner scopes than the usage of `finalize` or delete all of " + "existing `StateVector`.\n\n.. note:: This is " + "automatically called when the program exits. If you call this manually, you cannot use " + "most of scaluq's functions until the program exits."); + m.def("is_finalized", &is_initialized, "Return true if `finalize()` is already called."); +} +} // namespace internal +#endif } // namespace scaluq diff --git a/include/scaluq/operator/operator.hpp b/include/scaluq/operator/operator.hpp index 85bccad6..a26630c6 100644 --- a/include/scaluq/operator/operator.hpp +++ b/include/scaluq/operator/operator.hpp @@ -71,28 +71,23 @@ class Operator { #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_operator_operator_hpp(nb::module_& m) { - nb::class_>(m, "Operator", "General quantum operator class.") + nb::class_>(m, "Operator", "General quantum operator class.") .def(nb::init(), "qubit_count"_a, "Initialize operator with specified number of qubits.") - .def("is_hermitian", &Operator::is_hermitian, "Check if the operator is Hermitian.") - .def("n_qubits", - &Operator::n_qubits, - "Get the number of qubits the operator acts on.") - .def("terms", - &Operator::terms, - "Get the list of Pauli terms that make up the operator.") + .def("is_hermitian", &Operator::is_hermitian, "Check if the operator is Hermitian.") + .def("n_qubits", &Operator::n_qubits, "Get the number of qubits the operator acts on.") .def( - "to_string", &Operator::to_string, "Get string representation of the operator.") + "terms", &Operator::terms, "Get the list of Pauli terms that make up the operator.") + .def("to_string", &Operator::to_string, "Get string representation of the operator.") .def("add_operator", - nb::overload_cast&>(&Operator::add_operator), + nb::overload_cast&>(&Operator::add_operator), "Add a Pauli operator to this operator.") .def( "add_random_operator", - [](Operator& op, - std::uint64_t operator_count, - std::optional seed) { + [](Operator& op, std::uint64_t operator_count, std::optional seed) { return op.add_random_operator(operator_count, seed.value_or(std::random_device{}())); }, @@ -101,23 +96,21 @@ void bind_operator_operator_hpp(nb::module_& m) { "Add a specified number of random Pauli operators to this operator. An optional " "seed " "can be provided for reproducibility.") - .def("optimize", - &Operator::optimize, - "Optimize the operator by combining like terms.") + .def("optimize", &Operator::optimize, "Optimize the operator by combining like terms.") .def("get_dagger", - &Operator::get_dagger, + &Operator::get_dagger, "Get the adjoint (Hermitian conjugate) of the operator.") .def("apply_to_state", - &Operator::apply_to_state, + &Operator::apply_to_state, "Apply the operator to a state vector.") .def("get_expectation_value", - &Operator::get_expectation_value, + &Operator::get_expectation_value, "Get the expectation value of the operator with respect to a state vector.") .def("get_transition_amplitude", - &Operator::get_transition_amplitude, + &Operator::get_transition_amplitude, "Get the transition amplitude of the operator between two state vectors.") - .def(nb::self *= Complex()) - .def(nb::self * Complex()) + .def(nb::self *= Complex()) + .def(nb::self * Complex()) .def(+nb::self) .def(-nb::self) .def(nb::self += nb::self) @@ -126,12 +119,12 @@ void bind_operator_operator_hpp(nb::module_& m) { .def(nb::self - nb::self) .def(nb::self * nb::self) .def(nb::self *= nb::self) - .def(nb::self += PauliOperator()) - .def(nb::self + PauliOperator()) - .def(nb::self -= PauliOperator()) - .def(nb::self - PauliOperator()) - .def(nb::self *= PauliOperator()) - .def(nb::self * PauliOperator()); + .def(nb::self += PauliOperator()) + .def(nb::self + PauliOperator()) + .def(nb::self -= PauliOperator()) + .def(nb::self - PauliOperator()) + .def(nb::self *= PauliOperator()) + .def(nb::self * PauliOperator()); } } // namespace internal #endif diff --git a/include/scaluq/operator/pauli_operator.hpp b/include/scaluq/operator/pauli_operator.hpp index 9b0491df..63b38287 100644 --- a/include/scaluq/operator/pauli_operator.hpp +++ b/include/scaluq/operator/pauli_operator.hpp @@ -100,89 +100,86 @@ class PauliOperator { #ifdef SCALUQ_USE_NANOBIND namespace internal { +template void bind_operator_pauli_operator_hpp(nb::module_& m) { - nb::enum_::PauliID>(m, "PauliID") - .value("I", PauliOperator::I) - .value("X", PauliOperator::X) - .value("Y", PauliOperator::Y) - .value("Z", PauliOperator::Z) + nb::enum_::PauliID>(m, "PauliID") + .value("I", PauliOperator::I) + .value("X", PauliOperator::X) + .value("Y", PauliOperator::Y) + .value("Z", PauliOperator::Z) .export_values(); - nb::class_::Data>( + nb::class_::Data>( m, "PauliOperatorData", "Internal data structure for PauliOperator.") - .def(nb::init>(), "coef"_a = 1., "Initialize data with coefficient.") - .def(nb::init>(), + .def(nb::init>(), "coef"_a = 1., "Initialize data with coefficient.") + .def(nb::init>(), "pauli_string"_a, "coef"_a = 1., "Initialize data with pauli string.") .def(nb::init&, const std::vector&, - Complex>(), + Complex>(), "target_qubit_list"_a, "pauli_id_list"_a, "coef"_a = 1., "Initialize data with target qubits and pauli ids.") - .def(nb::init&, Complex>(), + .def(nb::init&, Complex>(), "pauli_id_par_qubit"_a, "coef"_a = 1., "Initialize data with pauli ids per qubit.") - .def(nb::init>(), + .def(nb::init>(), "bit_flip_mask"_a, "phase_flip_mask"_a, "coef"_a = 1., "Initialize data with bit flip and phase flip masks.") - .def(nb::init::Data&>(), + .def(nb::init::Data&>(), "data"_a, "Initialize pauli operator from Data object.") .def("add_single_pauli", - &PauliOperator::Data::add_single_pauli, + &PauliOperator::Data::add_single_pauli, "target_qubit"_a, "pauli_id"_a, "Add a single pauli operation to the data.") - .def("coef", - &PauliOperator::Data::coef, - "Get the coefficient of the Pauli operator.") + .def("coef", &PauliOperator::Data::coef, "Get the coefficient of the Pauli operator.") .def("set_coef", - &PauliOperator::Data::set_coef, + &PauliOperator::Data::set_coef, "c"_a, "Set the coefficient of the Pauli operator.") .def("target_qubit_list", - &PauliOperator::Data::target_qubit_list, + &PauliOperator::Data::target_qubit_list, "Get the list of target qubits.") - .def("pauli_id_list", - &PauliOperator::Data::pauli_id_list, - "Get the list of Pauli IDs.") + .def("pauli_id_list", &PauliOperator::Data::pauli_id_list, "Get the list of Pauli IDs.") .def("get_XZ_mask_representation", - &PauliOperator::Data::get_XZ_mask_representation, + &PauliOperator::Data::get_XZ_mask_representation, "Get the X and Z mask representation as a tuple of vectors."); - nb::class_>( + nb::class_>( m, "PauliOperator", "Pauli operator as coef and tensor product of single pauli for each qubit.") - .def(nb::init>(), + .def(nb::init>(), "coef"_a = 1., "Initialize operator which just multiplying coef.") .def(nb::init&, const std::vector&, - Complex>(), + Complex>(), "target_qubit_list"_a, "pauli_id_list"_a, "coef"_a = 1., "Initialize pauli operator. For each `i`, single pauli correspond to " "`pauli_id_list[i]` is applied to `target_qubit_list`-th qubit.") - .def(nb::init>(), + .def(nb::init>(), "pauli_string"_a, "coef"_a = 1., "Initialize pauli operator. If `pauli_string` is `\"X0Y2\"`, Pauli-X is applied to " "0-th qubit and Pauli-Y is applied to 2-th qubit. In `pauli_string`, spaces are " "ignored.") - .def(nb::init&, Complex>(), + .def(nb::init&, Complex>(), "pauli_id_par_qubit"_a, "coef"_a = 1., "Initialize pauli operator. For each `i`, single pauli correspond to " "`paul_id_per_qubit` is applied to `i`-th qubit.") - .def(nb::init>(), + .def(nb::init>(), "bit_flip_mask"_a, "phase_flip_mask"_a, "coef"_a = 1., @@ -192,39 +189,37 @@ void bind_operator_pauli_operator_hpp(nb::module_& m) { "csv-table::\n\n \"bit_flip\",\"phase_flip\",\"pauli\"\n " "\"0\",\"0\",\"I\"\n " "\"0\",\"1\",\"Z\"\n \"1\",\"0\",\"X\"\n \"1\",\"1\",\"Y\"") - .def("coef", &PauliOperator::coef, "Get property `coef`.") + .def("coef", &PauliOperator::coef, "Get property `coef`.") .def("target_qubit_list", - &PauliOperator::target_qubit_list, + &PauliOperator::target_qubit_list, "Get qubits to be applied pauli.") .def("pauli_id_list", - &PauliOperator::pauli_id_list, + &PauliOperator::pauli_id_list, "Get pauli id to be applied. The order is correspond to the result of " "`target_qubit_list`") .def("get_XZ_mask_representation", - &PauliOperator::get_XZ_mask_representation, + &PauliOperator::get_XZ_mask_representation, "Get single-pauli property as binary integer representation. See description of " "`__init__(bit_flip_mask_py: int, phase_flip_mask_py: int, coef: float=1.)` for " "details.") .def("get_pauli_string", - &PauliOperator::get_pauli_string, + &PauliOperator::get_pauli_string, "Get single-pauli property as string representation. See description of " "`__init__(pauli_string: str, coef: float=1.)` for details.") - .def("get_dagger", &PauliOperator::get_dagger, "Get adjoint operator.") + .def("get_dagger", &PauliOperator::get_dagger, "Get adjoint operator.") .def("get_qubit_count", - &PauliOperator::get_qubit_count, + &PauliOperator::get_qubit_count, "Get num of qubits to applied with, when count from 0-th qubit. Subset of $[0, " "\\mathrm{qubit_count})$ is the target.") - .def("apply_to_state", - &PauliOperator::apply_to_state, - "Apply pauli to state vector.") + .def("apply_to_state", &PauliOperator::apply_to_state, "Apply pauli to state vector.") .def("get_expectation_value", - &PauliOperator::get_expectation_value, + &PauliOperator::get_expectation_value, "Get expectation value of measuring state vector. $\\bra{\\psi}P\\ket{\\psi}$.") .def("get_transition_amplitude", - &PauliOperator::get_transition_amplitude, + &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()); } } // namespace internal #endif diff --git a/include/scaluq/state/state_vector.hpp b/include/scaluq/state/state_vector.hpp index 0de9024f..ab630316 100644 --- a/include/scaluq/state/state_vector.hpp +++ b/include/scaluq/state/state_vector.hpp @@ -90,17 +90,47 @@ class StateVector { namespace internal { template void bind_state_state_vector_hpp(nb::module_& m) { - nb::class_>( - m, - "StateVector", - "Vector representation of quantum state.\n\n.. note:: Qubit index is " - "start from 0. If the amplitudes of $\\ket{b_{n-1}\\dots b_0}$ is " - "$b_i$, the state is $\\sum_i b_i 2^i$.") + nb::class_>(m, + "StateVector", + DocString() + .desc("Vector representation of quantum state.") + .desc("Qubit index is " + "start from 0. If the i-th value of the vector is " + "$a_i$, the state is $\\sum_i a_i \\ket{i}$.") + .desc("Given `n_qubits: int`, construct with bases " + "$\\ket{0\\dots 0}$ holding `n_qubits` number of qubits.") + .ex(DocString::Code({">>> state1 = StateVector(2)", + ">>> print(state1)", + " *** Quantum State ***", + " * Qubit Count : 2", + " * Dimension : 4", + " * State vector : ", + "00: (1,0)", + "01: (0,0)", + "10: (0,0)", + "11: (0,0)", + ""})) + .build_as_google_style() + .c_str()) .def(nb::init(), - "Construct state vector with specified qubits, initialized with computational " - "basis $\\ket{0\\dots0}$.") - .def(nb::init&>(), - "Constructing state vector by copying other state.") + "n_qubits"_a, + DocString() + .desc("Construct with specified number of qubits.") + .desc("Vector is initialized with computational " + "basis $\\ket{0\\dots0}$.") + .arg("n_qubits", "int", "number of qubits") + .ex(DocString::Code({">>> state1 = StateVector(2)", + ">>> print(state1)", + " *** Quantum State ***", + " * Qubit Count : 2", + " * Dimension : 4", + " * State vector : ", + "00: (1,0)", + "01: (0,0)", + "10: (0,0)", + "11: (0,0)"})) + .build_as_google_style() + .c_str()) .def_static( "Haar_random_state", [](std::uint64_t n_qubits, std::optional seed) { @@ -145,53 +175,260 @@ void bind_state_state_vector_hpp(nb::module_& m) { .c_str()) .def("set_amplitude_at", &StateVector::set_amplitude_at, - "Manually set amplitude at one index.") + "index"_a, + "value"_a, + DocString() + .desc("Manually set amplitude at one index.") + .arg("index", + "int", + "index of state vector", + "This is read as binary.k-th bit of index represents k-th qubit.") + .arg("value", "complex", "amplitude value to set at index") + .ex(DocString::Code({">>> state = StateVector(2)", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, 0j, 0j]", + ">>> state.set_amplitude_at(2, 3+1j)", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, (3+1j), 0j]"})) + .note("If you want to get amplitudes at all indices, you should use " + ":meth:`.load`.") + .build_as_google_style() + .c_str()) .def("get_amplitude_at", &StateVector::get_amplitude_at, - "Get amplitude at one index.\n\n.. note:: If you want to get all amplitudes, you " - "should " - "use `StateVector::get_amplitudes()`.") + "index"_a, + DocString() + .desc("Get amplitude at one index.") + .arg("index", + "int", + "index of state vector", + "This is read as binary. k-th bit of index represents k-th qubit.") + .ret("complex", "Amplitude at specified index") + .ex(DocString::Code{">>> state = StateVector(2)", + ">>> state.load([1+2j, 3+4j, 5+6j, 7+8j])", + ">>> state.get_amplitude_at(0)", + "(1+2j)", + ">>> state.get_amplitude_at(1)", + "(3+4j)", + ">>> state.get_amplitude_at(2)", + "(5+6j)", + ">>> state.get_amplitude_at(3)", + "(7+8j)"}) + .note("If you want to get amplitudes at all indices, you should use " + ":meth:`.get_amplitudes`.") + .build_as_google_style() + .c_str()) .def("set_zero_state", &StateVector::set_zero_state, - "Initialize with computational basis $\\ket{00\\dots0}$.") + DocString() + .desc("Initialize with computational basis $\\ket{00\\dots0}$.") + .ex(DocString::Code{">>> state = StateVector.Haar_random_state(2)", + ">>> state.get_amplitudes()", + "[(-0.05726462181150916+0.3525270165415515j), " + "(0.1133709060491142+0.3074930854078303j), " + "(0.03542174692996924+0.18488950377672345j), " + "(0.8530024105558827+0.04459332470844164j)]", + ">>> state.set_zero_state()", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, 0j, 0j]"}) + .build_as_google_style() + .c_str()) .def("set_zero_norm_state", &StateVector::set_zero_norm_state, - "Initialize with 0 (null vector).") + DocString() + .desc("Initialize with 0 (null vector).") + .ex(DocString::Code{">>> state = StateVector(2)", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, 0j, 0j]", + ">>> state.set_zero_norm_state()", + ">>> state.get_amplitudes()", + "[0j, 0j, 0j, 0j]"}) + .build_as_google_style() + .c_str()) .def("set_computational_basis", &StateVector::set_computational_basis, - "Initialize with computational basis \\ket{\\mathrm{basis}}.") + "basis"_a, + DocString() + .desc("Initialize with computational basis \\ket{\\mathrm{basis}}.") + .arg("basis", + "int", + "basis as integer format ($0 \\leq \\mathrm{basis} \\leq " + "2^{\\mathrm{n\\_qubits}}-1$)") + .ex(DocString::Code{">>> state = StateVector(2)", + ">>> state.set_computational_basis(0) # |00>", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, 0j, 0j]", + ">>> state.set_computational_basis(1) # |01>", + ">>> state.get_amplitudes()", + "[0j, (1+0j), 0j, 0j]", + ">>> state.set_computational_basis(2) # |10>", + ">>> state.get_amplitudes()", + "[0j, 0j, (1+0j), 0j]", + ">>> state.set_computational_basis(3) # |11>", + ">>> state.get_amplitudes()", + "[0j, 0j, 0j, (1+0j)]"}) + .build_as_google_style() + .c_str()) .def("get_amplitudes", &StateVector::get_amplitudes, - "Get all amplitudes with as `list[complex]`.") - .def("n_qubits", &StateVector::n_qubits, "Get num of qubits.") + DocString() + .desc("Get all amplitudes as `list[complex]`.") + .ret("list[complex]", "amplitudes of list with len $2^{\\mathrm{n\\_qubits}}$") + .ex(DocString::Code{">>> state = StateVector(2)", + ">>> state.get_amplitudes()", + "[(1+0j), 0j, 0j, 0j]"}) + .build_as_google_style() + .c_str()) + .def("n_qubits", + &StateVector::n_qubits, + DocString() + .desc("Get num of qubits.") + .ret("int", "num of qubits") + .ex(DocString::Code{">>> state = StateVector(2)", ">>> state.n_qubits()", "2"}) + .build_as_google_style() + .c_str()) .def("dim", &StateVector::dim, - "Get dimension of the vector ($=2^\\mathrm{n\\_qubits}$).") + DocString() + .desc("Get dimension of the vector ($=2^\\mathrm{n\\_qubits}$).") + .ret("int", "dimension of the vector") + .ex(DocString::Code{">>> state = StateVector(2)", ">>> state.dim()", "4"}) + .build_as_google_style() + .c_str()) .def("get_squared_norm", &StateVector::get_squared_norm, - "Get squared norm of the state. $\\braket{\\psi|\\psi}$.") + DocString() + .desc("Get squared norm of the state. $\\braket{\\psi|\\psi}$.") + .ret("float", "squared norm of the state") + .ex(DocString::Code{">>> v = [1+2j, 3+4j, 5+6j, 7+8j]", + ">>> state = StateVector(2)", + ">>> state.load(v)", + ">>> state.get_squared_norm()", + "204.0" + ">>> sum([abs(a)**2 for a in v])", + "204.0"}) + .build_as_google_style() + .c_str()) .def("normalize", &StateVector::normalize, - "Normalize state (let $\\braket{\\psi|\\psi} = 1$ by multiplying coef).") - .def("get_zero_probability", - &StateVector::get_zero_probability, - "Get the probability to observe $\\ket{0}$ at specified index.") + DocString() + .desc("Normalize state.") + .desc("Let $\\braket{\\psi|\\psi} = 1$ by multiplying constant.") + .ex(DocString::Code{">>> v = [1+2j, 3+4j, 5+6j, 7+8j]", + ">>> state = StateVector(2)", + ">>> state.load(v)", + ">>> state.normalize()", + ">>> state.get_amplitudes()", + "[(0.07001400420140048+0.14002800840280097j), " + "(0.21004201260420147+0.28005601680560194j), " + "(0.3500700210070024+0.42008402520840293j), " + "(0.4900980294098034+0.5601120336112039j)]", + ">>> norm = state.get_squared_norm()**.5", + ">>> [a / norm for a in v]" + "[(0.07001400420140048+0.14002800840280097j), " + "(0.21004201260420147+0.28005601680560194j), " + "(0.3500700210070024+0.42008402520840293j), " + "(0.4900980294098034+0.5601120336112039j)]"}) + .build_as_google_style() + .c_str()) + .def( + "get_zero_probability", + &StateVector::get_zero_probability, + "index"_a, + DocString() + .desc("Get the probability to observe $\\ket{0}$ at specified index.") + .desc("**State must be normalized.**") + .arg("index", "int", "qubit index to be observed") + .ret("float", "probability to observe $\\ket{0}$") + .ex(DocString::Code{">>> v = [1 / 6**.5, 2j / 6**.5 * 1j, -1 / 6**.5, -2j / 6**.5]", + ">>> state = StateVector(2)", + ">>> state.load(v)", + ">>> state.get_zero_probability(0)", + "0.3333333333333334", + ">>> state.get_zero_probability(1)", + "0.8333333333333336", + ">>> abs(v[0])**2+abs(v[2])**2", + "0.3333333333333334", + ">>> abs(v[0])**2+abs(v[1])**2", + "0.8333333333333336"}) + .build_as_google_style() + .c_str()) .def("get_marginal_probability", &StateVector::get_marginal_probability, - "Get the marginal probability to observe as specified. Specify the result as n-length " - "list. `0` and `1` represent the qubit is observed and get the value. `2` represents " - "the qubit is not observed.") - .def("get_entropy", &StateVector::get_entropy, "Get the entropy of the vector.") + "measured_values"_a, + DocString() + .desc("Get the marginal probability to observe as given.") + .desc("**State must be normalized.**") + .arg("measured_values", + "list[int]", + "list with len n_qubits.", + "`0`, `1` or :attr:`.UNMEASURED` is allowed for each elements. `0` or `1` " + "shows the qubit is observed and the value is got. :attr:`.UNMEASURED` " + "shows the the qubit is not observed.") + .ret("float", "probability to observe as given") + .ex(DocString::Code{ + ">>> v = [1/4, 1/2, 0, 1/4, 1/4, 1/2, 1/4, 1/2]", + "state = StateVector(3)", + ">>> state.load(v)", + ">>> state.get_marginal_probability([0, 1, StateVector.UNMEASURED])", + "0.0625", + ">>> abs(v[2])**2 + abs(v[6])**2", + "0.0625"}) + .build_as_google_style() + .c_str()) + .def("get_entropy", + &StateVector::get_entropy, + DocString() + .desc("Get the entropy of the vector.") + .desc("**State must be normalized.**") + .ret("float", "entropy") + .ex(DocString::Code{ + ">>> v = [1/4, 1/2, 0, 1/4, 1/4, 1/2, 1/4, 1/2]", + ">>> state = StateVector(3)", + ">>> state.load(v)", + ">>> state.get_entropy()", + "2.5000000000000497", + ">>> sum(-abs(a)**2 * math.log2(abs(a)**2) for a in v if a != 0)", + "2.5"}) + .note("The result of this function differs from qulacs. This is because scaluq " + "adopted 2 for the base of log in the difinition of entropy $\\sum_i -p_i " + "\\log p_i$ " + "however qulacs adopted e.") + .build_as_google_style() + .c_str()) .def("add_state_vector_with_coef", &StateVector::add_state_vector_with_coef, - "add other state vector with multiplying the coef and make superposition. " - "$\\ket{\\mathrm{this}}\\leftarrow\\ket{\\mathrm{this}}+\\mathrm{coef}" - "\\ket{\\mathrm{" - "state}}$.") + "coef"_a, + "state"_a, + DocString() + .desc("Add other state vector with multiplying the coef and make superposition.") + .desc("$\\ket{\\mathrm{this}}\\leftarrow\\ket{\\mathrm{this}}+\\mathrm{coef} " + "\\ket{\\mathrm{state}}$.") + .arg("coef", "complex", "coefficient to multiply to `state`") + .arg("state", ":class:`StateVector`", "state to be added") + .ex(DocString::Code{">>> state1 = StateVector(1)", + ">>> state1.load([1, 2])", + ">>> state2 = StateVector(1)", + ">>> state2.load([3, 4])", + ">>> state1.add_state_vector_with_coef(2j, state2)", + ">>> state1.get_amplitudes()", + "[(1+6j), (2+8j)]"}) + .build_as_google_style() + .c_str()) .def("multiply_coef", &StateVector::multiply_coef, - "Multiply coef. " - "$\\ket{\\mathrm{this}}\\leftarrow\\mathrm{coef}\\ket{\\mathrm{this}}$.") + "coef"_a, + DocString() + .desc("Multiply coef.") + .desc("$\\ket{\\mathrm{this}}\\leftarrow\\mathrm{coef}\\ket{\\mathrm{this}}$.") + .arg("coef", "complex", "coefficient to multiply") + .ex(DocString::Code{">>> state = StateVector(1)", + ">>> state.load([1, 2])", + ">>> state.multiply_coef(2j)", + ">>> state.get_amplitudes()", + "[2j, 4j]"}) + .build_as_google_style() + .c_str()) .def( "sampling", [](const StateVector& state, @@ -201,14 +438,61 @@ void bind_state_state_vector_hpp(nb::module_& m) { }, "sampling_count"_a, "seed"_a = std::nullopt, - "Sampling specified times. Result is `list[int]` with the `sampling_count` length.") - .def("to_string", &StateVector::to_string, "Information as `str`.") - .def("load", &StateVector::load, "Load amplitudes of `list[int]` with `dim` length.") - .def("__str__", &StateVector::to_string, "Information as `str`.") - .def_ro_static("UNMEASURED", - &StateVector::UNMEASURED, - "Constant used for `StateVector::get_marginal_probability` to express the " - "the qubit is not measured."); + DocString() + .desc("Sampling state vector independently and get list of computational basis") + .arg("sampling_count", "int", "how many times to apply sampling") + .arg("seed", + "int | None", + true, + "random seed", + "If not specified, the value from random device is used.") + .ret("list[int]", + "result of sampling", + "list of `sampling_count` length. Each element is in " + "$[0,2^{\\mathrm{n\\_qubits}})$") + .ex(DocString::Code{" >>> state = StateVector(2)", + ">>> state.load([1/2, 0, -3**.5/2, 0])", + ">>> state.sampling(8) ", + "[0, 2, 2, 2, 2, 0, 0, 2]"}) + .build_as_google_style() + .c_str()) + .def( + "to_string", + &StateVector::to_string, + DocString() + .desc("Information as `str`.") + .ret("str", "information as str") + .ex(DocString::Code{ + ">>> state = StateVector(1)", + ">>> state.to_string()", + R"(' *** Quantum State ***\n * Qubit Count : 1\n * Dimension : 2\n * State vector : \n0: (1,0)\n1: (0,0)\n')"}) + .build_as_google_style() + .c_str()) + .def("load", + &StateVector::load, + "other"_a, + DocString() + .desc("Load amplitudes of `Sequence`") + .arg("other", + "collections.abc.Sequence[complex]", + "list of complex amplitudes with len $2^{\\mathrm{n_qubits}}$") + .build_as_google_style() + .c_str()) + .def("__str__", + &StateVector::to_string, + DocString() + .desc("Information as `str`.") + .desc("Same as :meth:`.to_string()`") + .build_as_google_style() + .c_str()) + .def_ro_static( + "UNMEASURED", + &StateVector::UNMEASURED, + DocString() + .desc("Constant used for `StateVector::get_marginal_probability` to express the " + "the qubit is not measured.") + .build_as_google_style() + .c_str()); } } // namespace internal #endif diff --git a/include/scaluq/types.hpp b/include/scaluq/types.hpp index ce070d5f..d3566acf 100644 --- a/include/scaluq/types.hpp +++ b/include/scaluq/types.hpp @@ -48,21 +48,4 @@ class SparseMatrix { SparseMatrix(const SparseComplexMatrix& sp); }; } // namespace internal - -#ifdef SCALUQ_USE_NANOBIND -namespace internal { -void bind_types_hpp(nb::module_& m) { - m.def("finalize", - &finalize, - "Terminate the Kokkos execution environment. Release the resources.\n\n.. note:: " - "Finalization fails if there exists `StateVector` allocated. You must use " - "`StateVector` only inside inner scopes than the usage of `finalize` or delete all of " - "existing `StateVector`.\n\n.. note:: This is " - "automatically called when the program exits. If you call this manually, you cannot use " - "most of scaluq's functions until the program exits."); - m.def("is_finalized", &is_initialized, "Return true if `finalize()` is already called."); -} -} // namespace internal -#endif - } // namespace scaluq diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 9236baba..40d91efd 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -21,12 +21,23 @@ nanobind_add_stub( MARKER_FILE scaluq/py.typed VERBOSE ) -nanobind_add_stub( - scaluq_stub - INSTALL_TIME - MODULE scaluq.scaluq_core.gate - OUTPUT scaluq/gate.pyi - PYTHON_PATH $ - MARKER_FILE scaluq/py.typed - VERBOSE -) +foreach(FLOAT IN ITEMS f64 f32) + nanobind_add_stub( + scaluq_stub + INSTALL_TIME + MODULE scaluq.scaluq_core.${FLOAT} + OUTPUT scaluq/${FLOAT}/__init__.pyi + PYTHON_PATH $ + MARKER_FILE scaluq/py.typed + VERBOSE + ) + nanobind_add_stub( + scaluq_stub + INSTALL_TIME + MODULE scaluq.scaluq_core.${FLOAT}.gate + OUTPUT scaluq/${FLOAT}/gate/__init__.pyi + PYTHON_PATH $ + MARKER_FILE scaluq/py.typed + VERBOSE + ) +endforeach() diff --git a/python/binding.cpp b/python/binding.cpp index 1722c623..3e3b582b 100644 --- a/python/binding.cpp +++ b/python/binding.cpp @@ -80,30 +80,41 @@ void cleanup() { if (!is_finalized()) finalize(); } -NB_MODULE(scaluq_core, m) { - internal::bind_types_hpp(m); - - internal::bind_state_state_vector_hpp(m); - internal::bind_state_state_vector_batched_hpp(m); - - auto mgate = m.def_submodule("gate", "Define gates."); - - internal::bind_gate_gate_hpp(m); - internal::bind_gate_gate_standard_hpp(m); - internal::bind_gate_gate_matrix_hpp(m); - internal::bind_gate_gate_pauli_hpp(m); - internal::bind_gate_gate_factory_hpp(mgate); - internal::bind_gate_param_gate_hpp(m); - internal::bind_gate_param_gate_standard_hpp(m); - internal::bind_gate_param_gate_pauli_hpp(m); - internal::bind_gate_param_gate_probablistic_hpp(m); - internal::bind_gate_param_gate_factory(mgate); - // internal::bind_gate_merge_gate_hpp(m); +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()); + + internal::bind_state_state_vector_hpp(mp); + internal::bind_state_state_vector_batched_hpp(mp); + + auto mgate = mp.def_submodule("gate", "Define gates."); + + internal::bind_gate_gate_hpp(mp); + internal::bind_gate_gate_standard_hpp(mp); + internal::bind_gate_gate_matrix_hpp(mp); + internal::bind_gate_gate_pauli_hpp(mp); + internal::bind_gate_gate_factory_hpp(mgate); + internal::bind_gate_param_gate_hpp(mp); + internal::bind_gate_param_gate_standard_hpp(mp); + internal::bind_gate_param_gate_pauli_hpp(mp); + internal::bind_gate_param_gate_probablistic_hpp(mp); + internal::bind_gate_param_gate_factory(mgate); + // internal::bind_gate_merge_gate_hpp(mp); + + internal::bind_circuit_circuit_hpp(mp); + + internal::bind_operator_pauli_operator_hpp(mp); + internal::bind_operator_operator_hpp(mp); +} - internal::bind_circuit_circuit_hpp(m); +NB_MODULE(scaluq_core, m) { + internal::bind_kokkos_hpp(m); + internal::bind_gate_gate_hpp_without_precision(m); - internal::bind_operator_pauli_operator_hpp(m); - internal::bind_operator_operator_hpp(m); + bind_on_precision(m, "f64"); + bind_on_precision(m, "f32"); initialize(); std::atexit(&cleanup); diff --git a/python/scaluq/__init__.py b/python/scaluq/__init__.py index ce30a569..9daedecf 100644 --- a/python/scaluq/__init__.py +++ b/python/scaluq/__init__.py @@ -1 +1,3 @@ from .scaluq_core import * +from . import f64 +from . import f32 diff --git a/python/scaluq/f32/__init__.py b/python/scaluq/f32/__init__.py new file mode 100644 index 00000000..f515c966 --- /dev/null +++ b/python/scaluq/f32/__init__.py @@ -0,0 +1,2 @@ +from ..scaluq_core.f32 import * +from . import gate diff --git a/python/scaluq/f32/gate/__init__.py b/python/scaluq/f32/gate/__init__.py new file mode 100644 index 00000000..d351ea51 --- /dev/null +++ b/python/scaluq/f32/gate/__init__.py @@ -0,0 +1 @@ +from ...scaluq_core.f32.gate import * diff --git a/python/scaluq/f64/__init__.py b/python/scaluq/f64/__init__.py new file mode 100644 index 00000000..d05af3b7 --- /dev/null +++ b/python/scaluq/f64/__init__.py @@ -0,0 +1,2 @@ +from ..scaluq_core.f64 import * +from . import gate diff --git a/python/scaluq/f64/gate/__init__.py b/python/scaluq/f64/gate/__init__.py new file mode 100644 index 00000000..ef7711b4 --- /dev/null +++ b/python/scaluq/f64/gate/__init__.py @@ -0,0 +1 @@ +from ...scaluq_core.f64.gate import * diff --git a/python/scaluq/gate.py b/python/scaluq/gate.py deleted file mode 100644 index 440ed3bd..00000000 --- a/python/scaluq/gate.py +++ /dev/null @@ -1 +0,0 @@ -from .scaluq_core.gate import *