Skip to content

Commit

Permalink
Merge pull request #184 from qulacs/python-precision
Browse files Browse the repository at this point in the history
Python precision
  • Loading branch information
KowerKoint authored Dec 13, 2024
2 parents ccd574e + 2f4ea1e commit 3ea7568
Show file tree
Hide file tree
Showing 28 changed files with 652 additions and 328 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,6 @@ jobs:
- name: Run / Test stub
if: ${{ matrix.device == 'cpu' }} # currently GPU runner is not supported
run: |
echo -e "from scaluq import StateVector, gate\nstate = StateVector(2)\nx = gate.X(0)\nx.update_quantum_state(state)\nprint(state.get_amplitudes())" > /tmp/sample.py
echo -e "from scaluq.f64 import StateVector, gate\nstate = StateVector(2)\nx = gate.X(0)\nx.update_quantum_state(state)\nprint(state.get_amplitudes())" > /tmp/sample.py
python /tmp/sample.py
python -m mypy /tmp/sample.py
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,17 @@ int main() {
scaluq::initialize(); // must be called before using any scaluq methods
{
const std::uint64_t n_qubits = 3;
scaluq::StateVector state = scaluq::StateVector::Haar_random_state(n_qubits, 0);
scaluq::StateVector<double> state = scaluq::StateVector::Haar_random_state(n_qubits, 0);
std::cout << state << std::endl;

scaluq::Circuit circuit(n_qubits);
scaluq::Circuit<double> circuit(n_qubits);
circuit.add_gate(scaluq::gate::X(0));
circuit.add_gate(scaluq::gate::CNot(0, 1));
circuit.add_gate(scaluq::gate::Y(1));
circuit.add_gate(scaluq::gate::RX(1, std::numbers::pi / 2));
circuit.update_quantum_state(state);

scaluq::Operator observable(n_qubits);
scaluq::Operator<double> observable(n_qubits);
observable.add_random_operator(1, 0);
auto value = observable.get_expectation_value(state);
std::cout << value << std::endl;
Expand All @@ -103,7 +103,7 @@ int main() {
## サンプルコード(Python)

```Python
from scaluq import *
from scaluq.f64 import *
import math

n_qubits = 3
Expand All @@ -122,3 +122,14 @@ value = observable.get_expectation_value(state)
print(value)

```

# 精度指定について
scaluqでは、計算に使用する浮動小数点数のサイズとして32bitと64bitが選択できます。
通常は64bitの使用が推奨されますが、量子機械学習での利用などあまり精度が必要でない場合は32bitを使用すると最大2倍程度の高速化が見込めます。

同じ精度のオブジェクト同士でしか演算を行うことができません。
例えば32bit用に作成したゲートでは64bitの`StateVector`を更新できません。

C++の場合、状態、ゲート、演算子、回路のクラスやゲートを生成する関数が、テンプレート引数を取るようになっており、そこに`float`または`double`を指定することで選択します。

Pythonの場合、精度に合わせて`scaluq.f32``scaluq.f64`のどちらかのサブモジュールからオブジェクトを`import`します。
43 changes: 20 additions & 23 deletions exe/main.cpp
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
#include <Kokkos_Core.hpp>
#include <functional>
#include <cstdint>
#include <iostream>
#include <memory>
#include <scaluq/circuit/circuit.hpp>
#include <scaluq/gate/gate_factory.hpp>
#include <scaluq/operator/operator.hpp>
#include <scaluq/state/state_vector.hpp>
#include <sstream>
#include <stdexcept>
#include <type_traits>
#include <vector>

using Fp = float;

int main() {
Kokkos::initialize();
scaluq::initialize(); // must be called before using any scaluq methods
{
std::uint64_t n_qubits = 3;
scaluq::StateVector<Fp> state(n_qubits);
state.load({0, 1, 2, 3, 4, 5, 6, 7});
/*
auto x_gate = scaluq::gate::X<Fp>(1, {0, 2});
x_gate->update_quantum_state(state);
auto sqrtx_gate = scaluq::gate::SqrtX<Fp>(1, {0});
sqrtx_gate->update_quantum_state(state);
auto sqrtxdag_gate = scaluq::gate::SqrtXdag<Fp>(0);
sqrtxdag_gate->update_quantum_state(state);
*/

const std::uint64_t n_qubits = 3;
scaluq::StateVector state = scaluq::StateVector<double>::Haar_random_state(n_qubits, 0);
std::cout << state << std::endl;

scaluq::Circuit<double> circuit(n_qubits);
circuit.add_gate(scaluq::gate::X<double>(0));
circuit.add_gate(scaluq::gate::CNot<double>(0, 1));
circuit.add_gate(scaluq::gate::Y<double>(1));
circuit.add_gate(scaluq::gate::RX<double>(1, std::numbers::pi / 2));
circuit.update_quantum_state(state);

scaluq::Operator<double> observable(n_qubits);
observable.add_random_operator(1, 0);
auto value = observable.get_expectation_value(state);
std::cout << value << std::endl;
}
Kokkos::finalize();
scaluq::finalize(); // must be called last
}
43 changes: 22 additions & 21 deletions include/scaluq/circuit/circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,55 +76,56 @@ class Circuit {

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
template <std::floating_point Fp>
void bind_circuit_circuit_hpp(nb::module_& m) {
nb::class_<Circuit<double>>(m, "Circuit", "Quantum circuit represented as gate array")
nb::class_<Circuit<Fp>>(m, "Circuit", "Quantum circuit represented as gate array")
.def(nb::init<std::uint64_t>(), "Initialize empty circuit of specified qubits.")
.def("n_qubits", &Circuit<double>::n_qubits, "Get property of `n_qubits`.")
.def("n_qubits", &Circuit<Fp>::n_qubits, "Get property of `n_qubits`.")
.def("gate_list",
&Circuit<double>::gate_list,
&Circuit<Fp>::gate_list,
"Get property of `gate_list`.",
nb::rv_policy::reference)
.def("n_gates", &Circuit<double>::n_gates, "Get property of `n_gates`.")
.def("key_set", &Circuit<double>::key_set, "Get set of keys of parameters.")
.def("get_gate_at", &Circuit<double>::get_gate_at, "Get reference of i-th gate.")
.def("n_gates", &Circuit<Fp>::n_gates, "Get property of `n_gates`.")
.def("key_set", &Circuit<Fp>::key_set, "Get set of keys of parameters.")
.def("get_gate_at", &Circuit<Fp>::get_gate_at, "Get reference of i-th gate.")
.def("get_param_key_at",
&Circuit<double>::get_param_key_at,
&Circuit<Fp>::get_param_key_at,
"Get parameter key of i-th gate. If it is not parametric, return None.")
.def("calculate_depth", &Circuit<double>::calculate_depth, "Get depth of circuit.")
.def("calculate_depth", &Circuit<Fp>::calculate_depth, "Get depth of circuit.")
.def("add_gate",
nb::overload_cast<const Gate<double>&>(&Circuit<double>::add_gate),
nb::overload_cast<const Gate<Fp>&>(&Circuit<Fp>::add_gate),
"Add gate. Given gate is copied.")
.def("add_param_gate",
nb::overload_cast<const ParamGate<double>&, std::string_view>(
&Circuit<double>::add_param_gate),
"Add parametric gate with specifing key. Given param_gate is copied.")
.def(
"add_param_gate",
nb::overload_cast<const ParamGate<Fp>&, std::string_view>(&Circuit<Fp>::add_param_gate),
"Add parametric gate with specifing key. Given param_gate is copied.")
.def("add_circuit",
nb::overload_cast<const Circuit<double>&>(&Circuit<double>::add_circuit),
nb::overload_cast<const Circuit<Fp>&>(&Circuit<Fp>::add_circuit),
"Add all gates in specified circuit. Given gates are copied.")
.def("update_quantum_state",
&Circuit<double>::update_quantum_state,
&Circuit<Fp>::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<double>& circuit, StateVector<double>& state, nb::kwargs kwargs) {
std::map<std::string, double> parameters;
[&](const Circuit<Fp>& circuit, StateVector<Fp>& state, nb::kwargs kwargs) {
std::map<std::string, Fp> parameters;
for (auto&& [key, param] : kwargs) {
parameters[nb::cast<std::string>(key)] = nb::cast<double>(param);
parameters[nb::cast<std::string>(key)] = nb::cast<Fp>(param);
}
circuit.update_quantum_state(state, parameters);
},
"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 "
"\"name=value\" format in kwargs.")
.def("update_quantum_state",
[](const Circuit<double>& circuit, StateVector<double>& state) {
[](const Circuit<Fp>& circuit, StateVector<Fp>& state) {
circuit.update_quantum_state(state);
})
.def("copy", &Circuit<double>::copy, "Copy circuit. All the gates inside is copied.")
.def("copy", &Circuit<Fp>::copy, "Copy circuit. All the gates inside is copied.")
.def("get_inverse",
&Circuit<double>::get_inverse,
&Circuit<Fp>::get_inverse,
"Get inverse of circuit. All the gates are newly created.");
}
} // namespace internal
Expand Down
18 changes: 11 additions & 7 deletions include/scaluq/gate/gate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,19 +342,20 @@ namespace internal {
[](const GATE_TYPE<FLOAT>& gate) { return gate->to_string(""); }, \
"Get string representation of the gate.")

nb::class_<Gate<double>> gate_base_def_double;
template <std::floating_point Fp>
nb::class_<Gate<Fp>> gate_base_def;

#define DEF_GATE(GATE_TYPE, FLOAT, DESCRIPTION) \
::scaluq::internal::gate_base_def_##FLOAT.def(nb::init<GATE_TYPE<FLOAT>>(), \
"Upcast from `" #GATE_TYPE "`."); \
::scaluq::internal::gate_base_def<FLOAT>.def(nb::init<GATE_TYPE<FLOAT>>(), \
"Upcast from `" #GATE_TYPE "`."); \
DEF_GATE_BASE( \
GATE_TYPE, \
FLOAT, \
DESCRIPTION \
"\n\n.. note:: Upcast is required to use gate-general functions (ex: add to Circuit).") \
.def(nb::init<Gate<FLOAT>>())

void bind_gate_gate_hpp(nb::module_& m) {
void bind_gate_gate_hpp_without_precision(nb::module_& m) {
nb::enum_<GateType>(m, "GateType", "Enum of Gate Type.")
.value("I", GateType::I)
.value("GlobalPhase", GateType::GlobalPhase)
Expand Down Expand Up @@ -383,13 +384,16 @@ void bind_gate_gate_hpp(nb::module_& m) {
.value("TwoTargetMatrix", GateType::TwoTargetMatrix)
.value("Pauli", GateType::Pauli)
.value("PauliRotation", GateType::PauliRotation);
}

gate_base_def_double =
template <std::floating_point Fp>
void bind_gate_gate_hpp(nb::module_& m) {
gate_base_def<Fp> =
DEF_GATE_BASE(Gate,
double,
Fp,
"General class of QuantumGate.\n\n.. note:: Downcast to requred to use "
"gate-specific functions.")
.def(nb::init<Gate<double>>(), "Just copy shallowly.");
.def(nb::init<Gate<Fp>>(), "Just copy shallowly.");
}
} // namespace internal
#endif
Expand Down
13 changes: 7 additions & 6 deletions include/scaluq/gate/gate_matrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,18 @@ using DenseMatrixGate = internal::GatePtr<internal::DenseMatrixGateImpl<Fp>>;

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
template <std::floating_point Fp>
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<double>& 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<double>& 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<double>& gate) { return gate->get_matrix(); })
.def("sparse_matrix",
[](const SparseMatrixGate<double>& gate) { return gate->get_sparse_matrix(); });
DEF_GATE(DenseMatrixGate, double, "Specific class of dense matrix gate.")
.def("matrix", [](const DenseMatrixGate<double>& gate) { return gate->get_matrix(); });
[](const SparseMatrixGate<Fp>& gate) { return gate->get_sparse_matrix(); });
DEF_GATE(DenseMatrixGate, Fp, "Specific class of dense matrix gate.")
.def("matrix", [](const DenseMatrixGate<Fp>& gate) { return gate->get_matrix(); });
}
} // namespace internal
#endif
Expand Down
5 changes: 3 additions & 2 deletions include/scaluq/gate/gate_pauli.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,15 @@ using PauliRotationGate = internal::GatePtr<internal::PauliRotationGateImpl<Fp>>

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
template <std::floating_point Fp>
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}$.");
}
Expand Down
7 changes: 4 additions & 3 deletions include/scaluq/gate/gate_probablistic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,19 @@ using ProbablisticGate = internal::GatePtr<internal::ProbablisticGateImpl<Fp>>;

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
template <std::floating_point Fp>
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<double>& gate) { return gate->gate_list(); },
[](const ProbablisticGate<Fp>& gate) { return gate->gate_list(); },
nb::rv_policy::reference)
.def(
"distribution",
[](const ProbablisticGate<double>& gate) { return gate->distribution(); },
[](const ProbablisticGate<Fp>& gate) { return gate->distribution(); },
nb::rv_policy::reference);
}
} // namespace internal
Expand Down
Loading

0 comments on commit 3ea7568

Please sign in to comment.