Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stubを分割 #169

Merged
merged 7 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ AccessModifierOffset: -4
BinPackArguments: false
BinPackParameters: false
ColumnLimit: 100
DerivePointerAlignment: false
PointerAlignment: Left
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format結果がブレるケースがあったので追加で指定しました。

5 changes: 5 additions & 0 deletions .github/workflows/ci_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ jobs:
- name: Test in Ubuntu
run: |
OMP_PROC_BIND=false ninja test -C build -j $(nproc)

- name: Test if stub exists
run: |
echo -e "from scaluq import StateVector\nfrom scaluq.gate import I" > /tmp/stub_sample.py
mypy /tmp/stub_sample.py
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全オブジェクトを列挙してmypyするテストも考えましたが、むしろ一部だけでも名前を指定したほうがそもそもなにもbindingできていない事故を検知できていいと思ったのでこうしました。
全オブジェクト列挙を手動でしておく方法は、追加忘れが起きそうなのと一部だけ生成されないケースをまだ見ていないので一旦やめました。


nvcc-build:
name: NVCC build
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ homepage = "http://www.scaluq.org"

[project.optional-dependencies]
dev = [
"mypy == 1.11.2",
"scikit-build == 0.17.6",
"typing_extensions == 4.12.0",
"numpy == 1.26.0",
"nanobind == 2.0.0"
]

ci = [
"mypy == 1.11.2",
"scikit-build == 0.17.6",
"typing_extensions == 4.12.0",
"numpy == 1.26.0",
Expand Down
842 changes: 24 additions & 818 deletions python/binding.cpp

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions scaluq/circuit/circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,58 @@ class Circuit {
void check_gate_is_valid(const Gate& gate) const;
void check_gate_is_valid(const ParamGate& gate) const;
};

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
void bind_circuit_circuit_hpp(nb::module_& m) {
nb::class_<Circuit>(m, "Circuit", "Quantum circuit represented as gate array")
.def(nb::init<std::uint64_t>(), "Initialize empty circuit of specified qubits.")
.def("n_qubits", &Circuit::n_qubits, "Get property of `n_qubits`.")
.def("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("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("add_gate",
nb::overload_cast<const Gate&>(&Circuit::add_gate),
"Add gate. Given gate is copied.")
.def("add_param_gate",
nb::overload_cast<const ParamGate&, std::string_view>(&Circuit::add_param_gate),
"Add parametric gate with specifing key. Given param_gate is copied.")
.def("add_circuit",
nb::overload_cast<const Circuit&>(&Circuit::add_circuit),
"Add all gates in specified circuit. Given gates are copied.")
.def("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<std::string, double> parameters;
for (auto&& [key, param] : kwargs) {
parameters[nb::cast<std::string>(key)] = nb::cast<double>(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& circuit, StateVector& state) { circuit.update_quantum_state(state); })
.def("copy", &Circuit::copy, "Copy circuit. All the gates inside is copied.")
.def("get_inverse",
&Circuit::get_inverse,
"Get inverse of circuit. All the gates are newly created.");
}
} // namespace internal
#endif
} // namespace scaluq
93 changes: 93 additions & 0 deletions scaluq/gate/gate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,97 @@ class GatePtr {
};
} // namespace internal

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
#define DEF_GATE_BASE(GATE_TYPE, DESCRIPTION) \
nb::class_<GATE_TYPE>(m, #GATE_TYPE, DESCRIPTION) \
.def("gate_type", &GATE_TYPE::gate_type, "Get gate type as `GateType` enum.") \
.def( \
"target_qubit_list", \
[](const GATE_TYPE& gate) { return gate->target_qubit_list(); }, \
"Get target qubits as `list[int]`. **Control qubits is not included.**") \
.def( \
"control_qubit_list", \
[](const GATE_TYPE& gate) { return gate->control_qubit_list(); }, \
"Get control qubits as `list[int]`.") \
.def( \
"operand_qubit_list", \
[](const GATE_TYPE& gate) { return gate->operand_qubit_list(); }, \
"Get target and control qubits as `list[int]`.") \
.def( \
"target_qubit_mask", \
[](const GATE_TYPE& gate) { return gate->target_qubit_mask(); }, \
"Get target qubits as mask. **Control qubits is not included.**") \
.def( \
"control_qubit_mask", \
[](const GATE_TYPE& gate) { return gate->control_qubit_mask(); }, \
"Get control qubits as mask.") \
.def( \
"operand_qubit_mask", \
[](const GATE_TYPE& gate) { return gate->operand_qubit_mask(); }, \
"Get target and control qubits as mask.") \
.def( \
"get_inverse", \
[](const GATE_TYPE& gate) { return gate->get_inverse(); }, \
"Generate inverse gate as `Gate` type. If not exists, return None.") \
.def( \
"update_quantum_state", \
[](const GATE_TYPE& gate, StateVector& state_vector) { \
gate->update_quantum_state(state_vector); \
}, \
"Apply gate to `state_vector`. `state_vector` in args is directly updated.") \
.def( \
"get_matrix", \
[](const GATE_TYPE& gate) { return gate->get_matrix(); }, \
"Get matrix representation of the gate.")

nb::class_<Gate> gate_base_def;

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

void bind_gate_gate_hpp(nb::module_& m) {
nb::enum_<GateType>(m, "GateType", "Enum of Gate Type.")
.value("I", GateType::I)
.value("GlobalPhase", GateType::GlobalPhase)
.value("X", GateType::X)
.value("Y", GateType::Y)
.value("Z", GateType::Z)
.value("H", GateType::H)
.value("S", GateType::S)
.value("Sdag", GateType::Sdag)
.value("T", GateType::T)
.value("Tdag", GateType::Tdag)
.value("SqrtX", GateType::SqrtX)
.value("SqrtXdag", GateType::SqrtXdag)
.value("SqrtY", GateType::SqrtY)
.value("SqrtYdag", GateType::SqrtYdag)
.value("P0", GateType::P0)
.value("P1", GateType::P1)
.value("RX", GateType::RX)
.value("RY", GateType::RY)
.value("RZ", GateType::RZ)
.value("U1", GateType::U1)
.value("U2", GateType::U2)
.value("U3", GateType::U3)
.value("OneTargetMatrix", GateType::OneTargetMatrix)
.value("Swap", GateType::Swap)
.value("TwoTargetMatrix", GateType::TwoTargetMatrix)
.value("Pauli", GateType::Pauli)
.value("PauliRotation", GateType::PauliRotation);

gate_base_def =
DEF_GATE_BASE(Gate,
"General class of QuantumGate.\n\n.. note:: Downcast to requred to use "
"gate-specific functions.")
.def(nb::init<Gate>(), "Just copy shallowly.");
}
} // namespace internal
#endif

} // namespace scaluq
189 changes: 188 additions & 1 deletion scaluq/gate/gate_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ inline Gate TwoTargetMatrix(std::uint64_t target1,
return internal::GateFactory::create_gate<internal::TwoTargetMatrixGateImpl>(
internal::vector_to_mask({target1, target2}), internal::vector_to_mask(controls), matrix);
}
// まだ
inline Gate Pauli(const PauliOperator& pauli, const std::vector<std::uint64_t>& controls = {}) {
auto tar = pauli.target_qubit_list();
return internal::GateFactory::create_gate<internal::PauliGateImpl>(
Expand Down Expand Up @@ -214,4 +213,192 @@ inline Gate Probablistic(const std::vector<double>& distribution,
gate_list);
}
} // namespace gate

#ifdef SCALUQ_USE_NANOBIND
namespace internal {
void bind_gate_gate_factory_hpp(nb::module_& mgate) {
mgate.def("I", &gate::I, "Generate general Gate class instance of I.");
mgate.def("GlobalPhase",
&gate::GlobalPhase,
"Generate general Gate class instance of GlobalPhase.",
"phase"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("X",
&gate::X,
"Generate general Gate class instance of X.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Y",
&gate::Y,
"Generate general Gate class instance of Y.",
"taget"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Z",
&gate::Z,
"Generate general Gate class instance of Z.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("H",
&gate::H,
"Generate general Gate class instance of H.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("S",
&gate::S,
"Generate general Gate class instance of S.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Sdag",
&gate::Sdag,
"Generate general Gate class instance of Sdag.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("T",
&gate::T,
"Generate general Gate class instance of T.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Tdag",
&gate::Tdag,
"Generate general Gate class instance of Tdag.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("SqrtX",
&gate::SqrtX,
"Generate general Gate class instance of SqrtX.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("SqrtXdag",
&gate::SqrtXdag,
"Generate general Gate class instance of SqrtXdag.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("SqrtY",
&gate::SqrtY,
"Generate general Gate class instance of SqrtY.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("SqrtYdag",
&gate::SqrtYdag,
"Generate general Gate class instance of SqrtYdag.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("P0",
&gate::P0,
"Generate general Gate class instance of P0.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("P1",
&gate::P1,
"Generate general Gate class instance of P1.",
"target"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("RX",
&gate::RX,
"Generate general Gate class instance of RX.",
"target"_a,
"angle"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("RY",
&gate::RY,
"Generate general Gate class instance of RY.",
"target"_a,
"angle"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("RZ",
&gate::RZ,
"Generate general Gate class instance of RZ.",
"target"_a,
"angle"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("U1",
&gate::U1,
"Generate general Gate class instance of U1.",
"target"_a,
"lambda_"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("U2",
&gate::U2,
"Generate general Gate class instance of U2.",
"target"_a,
"phi"_a,
"lambda_"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("U3",
&gate::U3,
"Generate general Gate class instance of U3.",
"target"_a,
"theta"_a,
"phi"_a,
"lambda_"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Swap",
&gate::Swap,
"Generate general Gate class instance of Swap.",
"target1"_a,
"target2"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def(
"CX",
&gate::CX,
"Generate general Gate class instance of CX.\n\n.. note:: CX is a specialization of X.");
mgate.def("CNot",
&gate::CX,
"Generate general Gate class instance of CNot.\n\n.. note:: CNot is an alias of CX.");
mgate.def(
"CZ",
&gate::CZ,
"Generate general Gate class instance of CZ.\n\n.. note:: CZ is a specialization of Z.");
mgate.def(
"CCX",
&gate::CCX,
"Generate general Gate class instance of CXX.\n\n.. note:: CX is a specialization of X.");
mgate.def(
"CCNot",
&gate::CCX,
"Generate general Gate class instance of CCNot.\n\n.. note:: CCNot is an alias of CCX.");
mgate.def("Toffoli",
&gate::CCX,
"Generate general Gate class instance of Toffoli.\n\n.. note:: Toffoli is an alias "
"of CCX.");
mgate.def("OneTargetMatrix",
&gate::OneTargetMatrix,
"Generate general Gate class instance of OneTargetMatrix.",
"target"_a,
"matrix"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("TwoTargetMatrix",
&gate::TwoTargetMatrix,
"Generate general Gate class instance of TwoTargetMatrix.",
"target1"_a,
"target2"_a,
"matrix"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("DenseMatrix",
&gate::DenseMatrix,
"Generate general Gate class instance of DenseMatrix. IGate, OneTargetMatrixGate or "
"TwoTargetMatrixGate correspond to len(target) is created. The case len(target) >= 3 "
"is currently not supported.",
"targets"_a,
"matrix"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Pauli",
&gate::Pauli,
"Generate general Gate class instance of Pauli.",
"pauli"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("PauliRotation",
&gate::PauliRotation,
"Generate general Gate class instance of PauliRotation.",
"pauli"_a,
"angle"_a,
"controls"_a = std::vector<std::uint64_t>{});
mgate.def("Probablistic",
&gate::Probablistic,
"Generate general Gate class instance of Probablistic.",
"distribution"_a,
"gate_list"_a);
}
} // namespace internal
#endif
} // namespace scaluq
Loading
Loading