From daca08ba6fab6cf19938ce431da63796e0395c63 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 10 Sep 2024 00:17:31 +0900 Subject: [PATCH 1/9] google style docstring --- main.py | 4 ++ python/binding.cpp | 2 + python/docstring.hpp | 100 ++++++++++++++++++++++++++++++++++ scaluq/state/state_vector.hpp | 46 +++++++++++++--- 4 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 main.py create mode 100644 python/docstring.hpp diff --git a/main.py b/main.py new file mode 100644 index 00000000..709a6e05 --- /dev/null +++ b/main.py @@ -0,0 +1,4 @@ +from scaluq import StateVector + +state = StateVector(2) +state.get_amplitude_at(2) diff --git a/python/binding.cpp b/python/binding.cpp index dcd732ae..34352f91 100644 --- a/python/binding.cpp +++ b/python/binding.cpp @@ -13,6 +13,8 @@ #include #include +#include "docstring.hpp" + namespace nb = nanobind; using namespace nb::literals; diff --git a/python/docstring.hpp b/python/docstring.hpp new file mode 100644 index 00000000..88c39b54 --- /dev/null +++ b/python/docstring.hpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +class DocString { +public: + using Code = std::vector; + using Block = std::variant; + using Blocks = std::vector; + struct Arg { + std::string_view name; + std::string_view type; + bool is_optional; + Blocks description; + }; + struct Ret { + std::string_view type; + Blocks description; + }; + + explicit DocString() {} + DocString& desc(const Block& desc) { + description.push_back(desc); + return *this; + } + DocString& arg(const Arg& arg) { + args.push_back(arg); + return *this; + } + DocString& ret(const Ret& ret) { + returns = ret; + return *this; + } + DocString& ex(const Block& ex) { + examples.push_back(ex); + return *this; + } + DocString& note(const Block& note) { + notes.push_back(note); + return *this; + } + + std::string build_as_google_style() { + std::ostringstream out; + auto output = [&](const Block& b, std::string_view indent) { + if (b.index() == 0) { + out << indent << std::get<0>(b) << "\n\n"; + } else { + const auto& code = std::get<1>(b); + for (const auto& line : code) { + out << indent << line << "\n"; + } + out << "\n"; + } + }; + for (const auto& b : description) { + output(b, ""); + } + if (!args.empty()) { + out << "Args:\n"; + for (const auto& arg : args) { + out << " " << arg.name << " (" << arg.type + << (arg.is_optional ? ", optional):\n" : "):\n"); + for (const auto& b : arg.description) { + output(b, " "); + } + } + } + if (returns.has_value()) { + out << "Returns:\n"; + const auto& ret = returns.value(); + out << " " << ret.type << ":\n"; + for (const auto& b : ret.description) { + output(b, " "); + } + } + if (!examples.empty()) { + out << "Examples:\n"; + for (const auto& b : examples) { + output(b, " "); + } + } + if (!notes.empty()) { + out << "Notes:\n"; + for (const auto& b : notes) { + output(b, " "); + } + } + return out.str(); + } + +private: + Blocks description; + std::vector args; + std::optional returns; + Blocks examples; + Blocks notes; +}; diff --git a/scaluq/state/state_vector.hpp b/scaluq/state/state_vector.hpp index 6ede5460..2230269d 100644 --- a/scaluq/state/state_vector.hpp +++ b/scaluq/state/state_vector.hpp @@ -78,12 +78,20 @@ namespace internal { 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$.") + 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}$.") + .build_as_google_style() + .c_str()) .def(nb::init(), - "Construct state vector with specified qubits, initialized with computational " - "basis $\\ket{0\\dots0}$.") + DocString() + .desc( + "Construct state vector with specified qubits, initialized with computational " + "basis $\\ket{0\\dots0}$.") + .build_as_google_style() + .c_str()) .def(nb::init(), "Constructing state vector by copying other state.") .def_static( "Haar_random_state", @@ -100,9 +108,29 @@ void bind_state_state_vector_hpp(nb::module_& m) { "Manually set amplitude at one index.") .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", + false, + {"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}$.") @@ -112,7 +140,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { .def("set_computational_basis", &StateVector::set_computational_basis, "Initialize with computational basis \\ket{\\mathrm{basis}}.") - .def("amplitudes", + .def("get_amplitudes", &StateVector::get_amplitudes, "Get all amplitudes with as `list[complex]`.") .def("n_qubits", &StateVector::n_qubits, "Get num of qubits.") From 3dfb2602f0d63a1694bf7233d21eedb2fa254442 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 10 Sep 2024 13:01:56 +0900 Subject: [PATCH 2/9] do not show __init__ doc --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 28dcc2b1..a735919d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -23,7 +23,7 @@ autoapi_dirs = ["./stub/scaluq"] autoapi_add_toctree_entry = True -autoapi_python_class_content = 'both' +autoapi_python_class_content = 'class' autoapi_options = [ "members", "undoc-members", From 8c6957b5d37395626b7fd63c777418ac67227a1d Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 10 Sep 2024 13:02:16 +0900 Subject: [PATCH 3/9] change entropy log base e->2 --- scaluq/state/state_vector.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scaluq/state/state_vector.cpp b/scaluq/state/state_vector.cpp index 440c2419..f09bba79 100644 --- a/scaluq/state/state_vector.cpp +++ b/scaluq/state/state_vector.cpp @@ -12,9 +12,9 @@ StateVector::StateVector(std::uint64_t n_qubits) set_zero_state(); } -void StateVector::set_amplitude_at(std::uint64_t index, const Complex& c) { +void StateVector::set_amplitude_at(std::uint64_t index, const Complex& value) { Kokkos::View host_view("single_value"); - host_view() = c; + host_view() = value; Kokkos::deep_copy(Kokkos::subview(_raw, index), host_view()); } @@ -156,7 +156,7 @@ double StateVector::get_entropy() const { KOKKOS_CLASS_LAMBDA(std::uint64_t idx, double& lsum) { double prob = internal::squared_norm(_raw[idx]); prob = (prob > eps) ? prob : eps; - lsum += -prob * Kokkos::log(prob); + lsum += -prob * Kokkos::log2(prob); }, ent); return ent; From a828408f6007e1e56d3c1ce32e1ab8d898900229 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 10 Sep 2024 13:02:38 +0900 Subject: [PATCH 4/9] bind some methods --- scaluq/state/state_vector.hpp | 312 ++++++++++++++++++++++++++++++---- 1 file changed, 281 insertions(+), 31 deletions(-) diff --git a/scaluq/state/state_vector.hpp b/scaluq/state/state_vector.hpp index 2230269d..1516d9a7 100644 --- a/scaluq/state/state_vector.hpp +++ b/scaluq/state/state_vector.hpp @@ -25,7 +25,7 @@ class StateVector { /** * @attention Very slow. You should use load() instead if you can. */ - void set_amplitude_at(std::uint64_t index, const Complex& c); + void set_amplitude_at(std::uint64_t index, const Complex& value); /** * @attention Very slow. You should use get_amplitudes() instead if you can. @@ -83,16 +83,40 @@ void bind_state_state_vector_hpp(nb::module_& m) { .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(), + "n_qubits"_a, DocString() - .desc( - "Construct state vector with specified qubits, initialized with computational " - "basis $\\ket{0\\dots0}$.") + .desc("Construct with specified number of qubits.") + .desc("Vector is initialized with computational " + "basis $\\ket{0\\dots0}$.") + .arg({"n_qubits", "int", false, {"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(), "Constructing state vector by copying other state.") .def_static( "Haar_random_state", [](std::uint64_t n_qubits, std::optional seed) { @@ -101,11 +125,61 @@ void bind_state_state_vector_hpp(nb::module_& m) { }, "n_qubits"_a, "seed"_a = std::nullopt, - "Constructing state vector with Haar random state. If seed is not specified, the value " - "from random device is used.") + DocString() + .desc("Construct :class:`StateVector` with Haar random state.") + .arg({"n_qubits", "int | None", false, {"number of qubits"}}) + .arg({"seed", + "int", + true, + {"random seed", "If not specified, the value from random device is used."}}) + .ex(DocString::Code( + {">>> state = StateVector.Haar_random_state(2)", + ">>> print(state.get_amplitudes())", + "[(-0.3188299516496241+0.6723250989136779j), " + "(-0.253461343768224-0.22430415678425403j), " + "(0.24998142919420457+0.33096908710840045j), " + "(0.2991187916479724+0.2650813322096342j)]", + ">>> print(StateVector.Haar_random_state(2).get_amplitudes()) # If seed is " + "not specified, generated vector differs.", + "[(-0.49336775961196616-0.3319437726884906j), " + "(-0.36069529482031787+0.31413708595210815j), " + "(-0.3654176892043237-0.10307602590749808j), " + "(-0.18175679804035652+0.49033467421609994j)]", + ">>> print(StateVector.Haar_random_state(2, 0).get_amplitudes())", + "[(0.030776817573663098-0.7321137912473642j), " + "(0.5679070655936114-0.14551095055034327j), " + "(-0.0932995615041323-0.07123201881040941j), " + "(0.15213024630399696-0.2871374092016799j)]", + ">>> print(StateVector.Haar_random_state(2, 0).get_amplitudes()) # If same " + "seed is specified, same vector is generated.", + "[(0.030776817573663098-0.7321137912473642j), " + "(0.5679070655936114-0.14551095055034327j), " + "(-0.0932995615041323-0.07123201881040941j), " + "(0.15213024630399696-0.2871374092016799j)]"})) + .build_as_google_style() + .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", + false, + {"index of state vector", + "This is read as binary.k-th bit of index represents k-th qubit."}}) + .arg({"value", "complex", false, {"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, "index"_a, @@ -114,7 +188,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { .arg({"index", "int", false, - {"Index of state vector.", + {"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)", @@ -133,44 +207,220 @@ void bind_state_state_vector_hpp(nb::module_& m) { .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", + false, + {"basis as integer format ($0 \\leq \\mathrm{basis} \\lea " + "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.") - .def("dim", &StateVector::dim, "Get dimension of the vector ($=2^\\mathrm{n\\_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, + 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", false, {"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]", + false, + {"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", &StateVector::add_state_vector, - "Add other state vector and make superposition. $\\ket{\\mathrm{this}} " - "\\leftarrow " - "\\ket{\\mathrm{this}} + \\ket{\\mathrm{state}}$.") + "state"_a, + DocString() + .desc("Add other state vector and make superposition.") + .desc("$\\ket{\\mathrm{this}} \\leftarrow \\ket{\\mathrm{this}} + " + "\\ket{\\mathrm{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(state2)", + ">>> state1.get_amplitudes()", + "[(4+0j), (6+0j)]"}) + .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. " From 353148c531e16a302c7e4e8af1cfa4de25eca7b4 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 17 Sep 2024 09:54:05 +0900 Subject: [PATCH 5/9] bind all StateVector methods --- scaluq/state/state_vector.hpp | 89 ++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/scaluq/state/state_vector.hpp b/scaluq/state/state_vector.hpp index 1516d9a7..b310dcfa 100644 --- a/scaluq/state/state_vector.hpp +++ b/scaluq/state/state_vector.hpp @@ -240,8 +240,8 @@ void bind_state_state_vector_hpp(nb::module_& m) { .arg({"basis", "int", false, - {"basis as integer format ($0 \\leq \\mathrm{basis} \\lea " - "2^{\\mathrm{n_qubits}}-1$)"}}) + {"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()", @@ -261,7 +261,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::get_amplitudes, DocString() .desc("Get all amplitudes as `list[complex]`.") - .ret({"list[complex]", {"amplitudes of list with len $2^{\\mathrm{n_qubits}}$"}}) + .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]"}) @@ -392,7 +392,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { .desc("Add other state vector and make superposition.") .desc("$\\ket{\\mathrm{this}} \\leftarrow \\ket{\\mathrm{this}} + " "\\ket{\\mathrm{state}}$.") - .arg({"state", ":class:`StateVector`", "state to be added"}) + .arg({"state", ":class:`StateVector`", false, {"state to be added"}}) .ex(DocString::Code{">>> state1 = StateVector(1)", ">>> state1.load([1, 2])", ">>> state2 = StateVector(1)", @@ -410,8 +410,8 @@ void bind_state_state_vector_hpp(nb::module_& m) { .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"}) + .arg({"coef", "complex", false, {"coefficient to multiply to `state`"}}) + .arg({"state", ":class:`StateVector`", false, {"state to be added"}}) .ex(DocString::Code{">>> state1 = StateVector(1)", ">>> state1.load([1, 2])", ">>> state2 = StateVector(1)", @@ -423,8 +423,18 @@ void bind_state_state_vector_hpp(nb::module_& m) { .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", false, {"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, @@ -434,14 +444,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", false, {"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]", + false, + {"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 From 01c9f41de230035e2f4955858c28e0f72bd0986f Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 17 Sep 2024 11:17:22 +0900 Subject: [PATCH 6/9] entropy test log e->2 --- tests/state/state_vector_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/state/state_vector_test.cpp b/tests/state/state_vector_test.cpp index e40356d1..69c5919d 100644 --- a/tests/state/state_vector_test.cpp +++ b/tests/state/state_vector_test.cpp @@ -170,7 +170,7 @@ TEST(StateVectorTest, EntropyCalculation) { for (std::uint64_t ind = 0; ind < dim; ++ind) { CComplex z = test_state[ind]; double prob = z.real() * z.real() + z.imag() * z.imag(); - if (prob > eps) ent += -prob * log(prob); + if (prob > eps) ent += -prob * log2(prob); } ASSERT_NEAR(ent, state.get_entropy(), eps); } From 37dfc0683353ee34e8d89f1d4e80432dc4c2ede9 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Tue, 17 Sep 2024 11:18:02 +0900 Subject: [PATCH 7/9] entropy batch log e->2 --- scaluq/state/state_vector_batched.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaluq/state/state_vector_batched.cpp b/scaluq/state/state_vector_batched.cpp index 1e9435b1..818289bc 100644 --- a/scaluq/state/state_vector_batched.cpp +++ b/scaluq/state/state_vector_batched.cpp @@ -280,7 +280,7 @@ std::vector StateVectorBatched::get_entropy() const { [&](std::uint64_t idx, double& lsum) { double prob = internal::squared_norm(_raw(batch_id, idx)); prob = Kokkos::max(prob, eps); - lsum += -prob * Kokkos::log(prob); + lsum += -prob * Kokkos::log2(prob); }, sum); team.team_barrier(); From 7d5773cfc815865801f5fc9fb3567011edea1661 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Fri, 20 Sep 2024 09:55:23 +0900 Subject: [PATCH 8/9] =?UTF-8?q?DocString=E3=81=AE=E7=94=9F=E6=88=90?= =?UTF-8?q?=E3=81=AB=E5=8F=AF=E5=A4=89=E9=95=B7=E5=BC=95=E6=95=B0=E3=82=92?= =?UTF-8?q?=E5=88=A9=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/docstring.hpp | 43 +++++++++++-- scaluq/state/state_vector.hpp | 111 +++++++++++++++++----------------- 2 files changed, 92 insertions(+), 62 deletions(-) diff --git a/python/docstring.hpp b/python/docstring.hpp index 88c39b54..d4c882d5 100644 --- a/python/docstring.hpp +++ b/python/docstring.hpp @@ -6,17 +6,17 @@ class DocString { public: - using Code = std::vector; - using Block = std::variant; + using Code = std::vector; + using Block = std::variant; using Blocks = std::vector; struct Arg { - std::string_view name; - std::string_view type; + std::string name; + std::string type; bool is_optional; Blocks description; }; struct Ret { - std::string_view type; + std::string type; Blocks description; }; @@ -29,10 +29,29 @@ class DocString { args.push_back(arg); return *this; } + template + DocString& arg(std::string_view name, std::string_view type, const Args&... desc) { + args.push_back(Arg(std::string(name), std::string(type), false, construct_blocks(desc...))); + return *this; + } + template + DocString& arg(std::string_view name, + std::string_view type, + bool is_optional, + const Args&... desc) { + args.push_back( + Arg(std::string(name), std::string(type), is_optional, construct_blocks(desc...))); + return *this; + } DocString& ret(const Ret& ret) { returns = ret; return *this; } + template + DocString& ret(std::string_view type, const Args&... desc) { + returns = Ret(std::string(type), construct_blocks(desc...)); + return *this; + } DocString& ex(const Block& ex) { examples.push_back(ex); return *this; @@ -97,4 +116,18 @@ class DocString { std::optional returns; Blocks examples; Blocks notes; + + Blocks construct_blocks_reverse() { return Blocks(); } + template + Blocks construct_blocks_reverse(const Block& block, const Tail&... tail) { + Blocks blocks = construct_blocks_reverse(tail...); + blocks.push_back(block); + return blocks; + } + template + Blocks construct_blocks(const Args&... args) { + Blocks blocks = construct_blocks_reverse(args...); + std::ranges::reverse(blocks); + return blocks; + } }; diff --git a/scaluq/state/state_vector.hpp b/scaluq/state/state_vector.hpp index b310dcfa..92deaa00 100644 --- a/scaluq/state/state_vector.hpp +++ b/scaluq/state/state_vector.hpp @@ -104,7 +104,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { .desc("Construct with specified number of qubits.") .desc("Vector is initialized with computational " "basis $\\ket{0\\dots0}$.") - .arg({"n_qubits", "int", false, {"number of qubits"}}) + .arg("n_qubits", "int", "number of qubits") .ex(DocString::Code({">>> state1 = StateVector(2)", ">>> print(state1)", " *** Quantum State ***", @@ -127,11 +127,12 @@ void bind_state_state_vector_hpp(nb::module_& m) { "seed"_a = std::nullopt, DocString() .desc("Construct :class:`StateVector` with Haar random state.") - .arg({"n_qubits", "int | None", false, {"number of qubits"}}) - .arg({"seed", - "int", - true, - {"random seed", "If not specified, the value from random device is used."}}) + .arg("n_qubits", "int", "number of qubits") + .arg("seed", + "int | None", + true, + "random seed", + "If not specified, the value from random device is used.") .ex(DocString::Code( {">>> state = StateVector.Haar_random_state(2)", ">>> print(state.get_amplitudes())", @@ -164,12 +165,11 @@ void bind_state_state_vector_hpp(nb::module_& m) { "value"_a, DocString() .desc("Manually set amplitude at one index.") - .arg({"index", - "int", - false, - {"index of state vector", - "This is read as binary.k-th bit of index represents k-th qubit."}}) - .arg({"value", "complex", false, {"amplitude value to set at 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]", @@ -185,12 +185,11 @@ void bind_state_state_vector_hpp(nb::module_& m) { "index"_a, DocString() .desc("Get amplitude at one index.") - .arg({"index", - "int", - false, - {"index of state vector", - "This is read as binary. k-th bit of index represents k-th qubit."}}) - .ret({"complex", {"Amplitude at specified 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)", @@ -237,11 +236,10 @@ void bind_state_state_vector_hpp(nb::module_& m) { "basis"_a, DocString() .desc("Initialize with computational basis \\ket{\\mathrm{basis}}.") - .arg({"basis", - "int", - false, - {"basis as integer format ($0 \\leq \\mathrm{basis} \\leq " - "2^{\\mathrm{n\\_qubits}}-1$)"}}) + .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()", @@ -261,7 +259,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::get_amplitudes, DocString() .desc("Get all amplitudes as `list[complex]`.") - .ret({"list[complex]", {"amplitudes of list with len $2^{\\mathrm{n\\_qubits}}$"}}) + .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]"}) @@ -271,7 +269,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::n_qubits, DocString() .desc("Get num of qubits.") - .ret({"int", {"num of qubits"}}) + .ret("int", "num of qubits") .ex(DocString::Code{">>> state = StateVector(2)", ">>> state.n_qubits()", "2"}) .build_as_google_style() .c_str()) @@ -279,7 +277,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::dim, DocString() .desc("Get dimension of the vector ($=2^\\mathrm{n\\_qubits}$).") - .ret({"int", {"dimension of the vector"}}) + .ret("int", "dimension of the vector") .ex(DocString::Code{">>> state = StateVector(2)", ">>> state.dim()", "4"}) .build_as_google_style() .c_str()) @@ -287,7 +285,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::get_squared_norm, DocString() .desc("Get squared norm of the state. $\\braket{\\psi|\\psi}$.") - .ret({"float", {"squared norm of the state"}}) + .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)", @@ -326,8 +324,8 @@ void bind_state_state_vector_hpp(nb::module_& m) { DocString() .desc("Get the probability to observe $\\ket{0}$ at specified index.") .desc("**State must be normalized.**") - .arg({"index", "int", false, {"qubit index to be observed"}}) - .ret({"float", {"probability to observe $\\ket{0}$"}}) + .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)", @@ -347,14 +345,13 @@ void bind_state_state_vector_hpp(nb::module_& m) { DocString() .desc("Get the marginal probability to observe as given.") .desc("**State must be normalized.**") - .arg({"measured_values", - "list[int]", - false, - {"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"}}) + .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)", @@ -370,7 +367,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { DocString() .desc("Get the entropy of the vector.") .desc("**State must be normalized.**") - .ret({"float", {"entropy"}}) + .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)", @@ -392,7 +389,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { .desc("Add other state vector and make superposition.") .desc("$\\ket{\\mathrm{this}} \\leftarrow \\ket{\\mathrm{this}} + " "\\ket{\\mathrm{state}}$.") - .arg({"state", ":class:`StateVector`", false, {"state to be added"}}) + .arg("state", ":class:`StateVector`", "state to be added") .ex(DocString::Code{">>> state1 = StateVector(1)", ">>> state1.load([1, 2])", ">>> state2 = StateVector(1)", @@ -410,8 +407,8 @@ void bind_state_state_vector_hpp(nb::module_& m) { .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", false, {"coefficient to multiply to `state`"}}) - .arg({"state", ":class:`StateVector`", false, {"state to be added"}}) + .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)", @@ -427,7 +424,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { DocString() .desc("Multiply coef.") .desc("$\\ket{\\mathrm{this}}\\leftarrow\\mathrm{coef}\\ket{\\mathrm{this}}$.") - .arg({"coef", "complex", false, {"coefficient to multiply"}}) + .arg("coef", "complex", "coefficient to multiply") .ex(DocString::Code{">>> state = StateVector(1)", ">>> state.load([1, 2])", ">>> state.multiply_coef(2j)", @@ -446,18 +443,19 @@ void bind_state_state_vector_hpp(nb::module_& m) { "seed"_a = std::nullopt, DocString() .desc("Sampling state vector independently and get list of computational basis") - .arg({"sampling_count", "int", false, {"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}})$"}}) + .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) ", + ">>> state.sampling(8) ", "[0, 2, 2, 2, 2, 0, 0, 2]"}) .build_as_google_style() .c_str()) @@ -466,7 +464,7 @@ void bind_state_state_vector_hpp(nb::module_& m) { &StateVector::to_string, DocString() .desc("Information as `str`.") - .ret({"str", {"information as str"}}) + .ret("str", "information as str") .ex(DocString::Code{ ">>> state = StateVector(1)", ">>> state.to_string()", @@ -478,10 +476,9 @@ void bind_state_state_vector_hpp(nb::module_& m) { "other"_a, DocString() .desc("Load amplitudes of `Sequence`") - .arg({"other", - "collections.abc.Sequence[complex]", - false, - {"list of complex amplitudes with len $2^{\\mathrm{n_qubits}}$"}}) + .arg("other", + "collections.abc.Sequence[complex]", + "list of complex amplitudes with len $2^{\\mathrm{n_qubits}}$") .build_as_google_style() .c_str()) .def("__str__", From 9f18451d666d0d2efe60404e9e0773e694448639 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Fri, 20 Sep 2024 10:00:56 +0900 Subject: [PATCH 9/9] =?UTF-8?q?log2=E3=82=92stdc++=E3=81=AE=E3=82=82?= =?UTF-8?q?=E3=81=AE=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/state/state_vector_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/state/state_vector_test.cpp b/tests/state/state_vector_test.cpp index 69c5919d..054fa60f 100644 --- a/tests/state/state_vector_test.cpp +++ b/tests/state/state_vector_test.cpp @@ -170,7 +170,7 @@ TEST(StateVectorTest, EntropyCalculation) { for (std::uint64_t ind = 0; ind < dim; ++ind) { CComplex z = test_state[ind]; double prob = z.real() * z.real() + z.imag() * z.imag(); - if (prob > eps) ent += -prob * log2(prob); + if (prob > eps) ent += -prob * std::log2(prob); } ASSERT_NEAR(ent, state.get_entropy(), eps); }