Skip to content

Commit 4df9939

Browse files
authored
Update libraries for cudaq::spin_op breaking changes (#134)
This PR includes the required CUDA-QX changes that must be applied once NVIDIA/cuda-quantum#2710 (from CUDA-Q) lands. For that PR, most of the breaking changes are in C++, but a few Python tests needed to be updated as well. Python changes will likely be coming in the near future. Note that this change actually removes more code than it adds. This is primarily due to updates in the test code. Here is a summary of the changes in this PR. 1. Update CMakeLists.txt files to link against `cudaq-operator` instead of `cudaq-spin` because `cuda-spin` was removed. 2. Make use of `cudaq::spin_op_term` (a product term) where appropriate. I currently left all the public QEC API's to use `spin_op` (a sum term), but we may choose to update them to simply `spin_op_term` in the near future. That would be a CUDA-QX breaking change if we do. 3. Since the new operators no longer store degrees that had implicit `I`'s in them, we must now clarify whether we want `I` in the terms. For QEC, this means that we will sometimes use something like `cudaq::spin_op_term identity(0, get_num_data_qubits())`, but for Solvers, we have eliminated all implicit `I`'s. 4. Use `get_pauli_word()` instead of `to_string(false)`. 5. Use `evaluate_coefficient()` instead of `get_coefficient()`. 6. Use `.trim()` to remove 0-cofficient terms and `.canonicalize()` to remove `I` operations from terms. (These new helper functions help reduce boiler-plate code.) 7. For default-constructed `cudaq::spin_op` objects, clarify whether you want them to be initialized to the neutral sum term (i.e. 0 = `spin_op::empty()`) or the neutral product term (i.e. 1 = `spin_op::identity()`). 8. Change `for_each_term()` to use iterators like `for (const auto &term : hamiltonian)`. 9. As needed, update code to reflect that `get_qubit_count()` now only returns the number of _actual_ degrees used in the operator rather than the "highest degree + 1" that used to be returned. If needed, make use of the new `min_degree()` and `max_degree()` methods instead. 10. For canned test data, update to use the new serialization format (as retrieved by `get_data_representation()`). 11. For test data comparisons, be sure to call `canonicalize` on the test data and/or truth data as necessary in order to ensure correct comparisons. 12. Don't use `get_raw_data()` anymore. --------- Signed-off-by: Ben Howe <[email protected]>
1 parent 982df58 commit 4df9939

File tree

32 files changed

+365
-664
lines changed

32 files changed

+365
-664
lines changed

.cudaq_version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"cudaq": {
33
"repository": "NVIDIA/cuda-quantum",
4-
"ref": "3ac999f0e34a2e84989c6fc80418f93feec6784f"
4+
"ref": "c53f77436f306f19e412015049dfafb7ae80a690"
55
}
66
}

.github/workflows/scripts/build_cudaq.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ index 7b7541d..2261334 100644
115115
if (NOT Python_FOUND)
116116
message(FATAL_ERROR "find_package(Python) not run?")
117117
endif()
118-
- target_link_libraries(cudaq-pyscf PRIVATE Python::Python pybind11::pybind11 cudaq-chemistry cudaq-spin cudaq cudaq-py-utils)
119-
+ target_link_libraries(cudaq-pyscf PRIVATE Python::Module pybind11::pybind11 cudaq-chemistry cudaq-spin cudaq cudaq-py-utils)
118+
- target_link_libraries(cudaq-pyscf PRIVATE Python::Python pybind11::pybind11 cudaq-chemistry cudaq-operator cudaq cudaq-py-utils)
119+
+ target_link_libraries(cudaq-pyscf PRIVATE Python::Module pybind11::pybind11 cudaq-chemistry cudaq-operator cudaq cudaq-py-utils)
120120
endif()
121121
install(TARGETS cudaq-pyscf DESTINATION lib/plugins)'
122122

libs/qec/lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ target_link_libraries(${LIBRARY_NAME}
4343
PUBLIC
4444
cudaqx-core
4545
cudaq::cudaq
46-
cudaq::cudaq-spin
46+
cudaq::cudaq-operator
4747
PRIVATE
4848
cudaq::cudaq-common
4949
)

libs/qec/lib/codes/repetition.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/****************************************************************-*- C++ -*-****
2-
* Copyright (c) 2024 NVIDIA Corporation & Affiliates. *
1+
/*******************************************************************************
2+
* Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. *
33
* All rights reserved. *
44
* *
55
* This source code and the accompanying materials are made available under *
@@ -31,17 +31,19 @@ repetition::repetition(const heterogeneous_map &options) : code() {
3131
operation_encodings.insert(std::make_pair(operation::prep0, prep0));
3232
operation_encodings.insert(std::make_pair(operation::prep1, prep1));
3333

34+
// "IIII..." (identity term for each data qubit)
35+
cudaq::spin_op_term identity(0, get_num_data_qubits());
36+
3437
// Default Stabilizers should be Zi-1 Zi
3538
for (std::size_t i = 1; i < get_num_data_qubits(); i++) {
36-
m_stabilizers.push_back(cudaq::spin::i(get_num_data_qubits() - 1) *
37-
cudaq::spin::z(i - 1) * cudaq::spin::z(i));
39+
m_stabilizers.push_back(identity * cudaq::spin::z(i - 1) *
40+
cudaq::spin::z(i));
3841
}
3942

4043
// Default Logical Observable is ZI...I
4144
// This class is only for Z basis experiments
4245
// so there is no X observable included.
43-
cudaq::spin_op Lz = cudaq::spin::z(0);
44-
Lz = Lz * cudaq::spin::i(get_num_data_qubits() - 1);
46+
cudaq::spin_op Lz = cudaq::spin::z(0) * identity;
4547

4648
m_pauli_observables.push_back(Lz);
4749

libs/qec/lib/decoders/plugins/example/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ target_link_libraries(${MODULE_NAME}
3333
PUBLIC
3434
cudaqx-core
3535
cudaq::cudaq
36-
cudaq::cudaq-spin
36+
cudaq::cudaq-operator
3737
PRIVATE
3838
cudaq::cudaq-common
3939
cudaq-qec

libs/qec/lib/stabilizer_utils.cpp

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ namespace cudaq::qec {
1111
// Sort the stabilizers by the occurrence of 'Z' first, then by 'X'.
1212
// An earlier occurrence is considered "less".
1313
// Example: a = "ZZX", b = "XXZ" -> a < b
14-
static bool spinOpComparator(const cudaq::spin_op &a, const cudaq::spin_op &b) {
15-
auto astr = a.to_string(false);
16-
auto bstr = b.to_string(false);
14+
static bool spinOpComparatorStr(const std::string &astr,
15+
const std::string &bstr) {
1716
auto zIdxA = astr.find_first_of("Z");
1817
auto zIdxB = bstr.find_first_of("Z");
1918
if (zIdxA == std::string::npos) {
@@ -32,6 +31,12 @@ static bool spinOpComparator(const cudaq::spin_op &a, const cudaq::spin_op &b) {
3231
return zIdxA < zIdxB;
3332
}
3433

34+
static bool spinOpComparator(const cudaq::spin_op &a, const cudaq::spin_op &b) {
35+
auto astr = a.begin()->get_pauli_word();
36+
auto bstr = b.begin()->get_pauli_word();
37+
return spinOpComparatorStr(astr, bstr);
38+
}
39+
3540
static bool isStabilizerSorted(const std::vector<cudaq::spin_op> &ops) {
3641
return std::is_sorted(ops.begin(), ops.end(), spinOpComparator);
3742
}
@@ -49,43 +54,36 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
4954
if (stabilizers.empty())
5055
return cudaqx::tensor<uint8_t>();
5156

52-
// Stabilizers must be sorted prior to use, but they are "const" into this
53-
// function. First check to see if they are already sorted, and if so, proceed
54-
// without making any copies. Otherwise make a copy, sort the copy, and make
55-
// the downstream code use the sorted copy.
56-
const std::vector<cudaq::spin_op> *p_stabilizers = &stabilizers;
57-
std::vector<cudaq::spin_op> stabilizers_copy; // only use if necessary
58-
if (!isStabilizerSorted(stabilizers)) {
59-
// Make a copy and sort them
60-
stabilizers_copy = stabilizers;
61-
sortStabilizerOps(stabilizers_copy);
62-
p_stabilizers = &stabilizers_copy;
63-
}
57+
std::vector<std::string> stab_strings;
58+
for (auto &s : stabilizers)
59+
stab_strings.emplace_back(s.begin()->get_pauli_word());
60+
std::sort(stab_strings.begin(), stab_strings.end(), spinOpComparatorStr);
6461

62+
auto numQubits = stab_strings[0].size();
63+
auto numStabilizers = stab_strings.size();
6564
if (type == stabilizer_type::XZ) {
66-
auto numQubits = (*p_stabilizers)[0].num_qubits();
67-
cudaqx::tensor<uint8_t> t({p_stabilizers->size(), 2 * numQubits});
65+
cudaqx::tensor<uint8_t> t({numStabilizers, 2 * numQubits});
6866
// Start by counting the number of Z spin_ops
6967
std::size_t numZRows = 0;
70-
for (auto &op : *p_stabilizers)
71-
if (op.to_string(false).find("Z") != std::string::npos)
68+
for (const auto &op_str : stab_strings)
69+
if (op_str.find("Z") != std::string::npos)
7270
numZRows++;
7371
else
7472
break;
7573

7674
// Need to shift Z bits left
7775
for (std::size_t row = 0; row < numZRows; row++) {
7876
for (std::size_t i = numQubits; i < 2 * numQubits; i++) {
79-
if ((*p_stabilizers)[row].get_raw_data().first[0][i])
77+
if (stab_strings[row][i - numQubits] == 'Z')
8078
t.at({row, i - numQubits}) = 1;
8179
}
8280
}
8381

84-
auto numXRows = p_stabilizers->size() - numZRows;
82+
auto numXRows = numStabilizers - numZRows;
8583

8684
for (std::size_t row = 0; row < numXRows; row++) {
8785
for (std::size_t i = 0; i < numQubits; i++) {
88-
if ((*p_stabilizers)[numZRows + row].get_raw_data().first[0][i])
86+
if (stab_strings[numZRows + row][i] == 'X')
8987
t.at({numZRows + row, i + numQubits}) = 1;
9088
}
9189
}
@@ -94,11 +92,10 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
9492
}
9593

9694
if (type == stabilizer_type::Z) {
97-
auto numQubits = (*p_stabilizers)[0].num_qubits();
9895
// Start by counting the number of Z spin_ops
9996
std::size_t numZRows = 0;
100-
for (auto &op : *p_stabilizers)
101-
if (op.to_string(false).find("Z") != std::string::npos)
97+
for (const auto &op_str : stab_strings)
98+
if (op_str.find("Z") != std::string::npos)
10299
numZRows++;
103100
else
104101
break;
@@ -109,32 +106,31 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
109106
cudaqx::tensor<uint8_t> ret({numZRows, numQubits});
110107
for (std::size_t row = 0; row < numZRows; row++) {
111108
for (std::size_t i = numQubits; i < 2 * numQubits; i++) {
112-
if ((*p_stabilizers)[row].get_raw_data().first[0][i])
109+
if (stab_strings[row][i - numQubits] == 'Z')
113110
ret.at({row, i - numQubits}) = 1;
114111
}
115112
}
116113

117114
return ret;
118115
}
119116

120-
auto numQubits = (*p_stabilizers)[0].num_qubits();
121117
// Start by counting the number of Z spin_ops
122118
std::size_t numZRows = 0;
123-
for (auto &op : *p_stabilizers)
124-
if (op.to_string(false).find("Z") != std::string::npos)
119+
for (const auto &op_str : stab_strings)
120+
if (op_str.find("Z") != std::string::npos)
125121
numZRows++;
126122
else
127123
break;
128124

129-
auto numXRows = p_stabilizers->size() - numZRows;
125+
auto numXRows = numStabilizers - numZRows;
130126

131127
if (numXRows == 0)
132128
return cudaqx::tensor<uint8_t>();
133129

134130
cudaqx::tensor<uint8_t> ret({numXRows, numQubits});
135131
for (std::size_t row = 0; row < numXRows; row++) {
136132
for (std::size_t i = 0; i < numQubits; i++) {
137-
if ((*p_stabilizers)[numZRows + row].get_raw_data().first[0][i])
133+
if (stab_strings[numZRows + row][i] == 'X')
138134
ret.at({row, i}) = 1;
139135
}
140136
}

libs/qec/python/bindings/type_casters.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/*******************************************************************************
2-
* Copyright (c) 2022 - 2023 NVIDIA Corporation & Affiliates. *
1+
/****************************************************************-*- C++ -*-****
2+
* Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. *
33
* All rights reserved. *
44
* *
55
* This source code and the accompanying materials are made available under *
@@ -23,14 +23,14 @@ struct type_caster<cudaq::spin_op> {
2323
return false;
2424
auto data = src.attr("serialize")().cast<std::vector<double>>();
2525
auto numQubits = src.attr("get_qubit_count")().cast<std::size_t>();
26-
value = cudaq::spin_op(data, numQubits);
26+
value = cudaq::spin_op(data);
2727
return true;
2828
}
2929

3030
static handle cast(cudaq::spin_op v, return_value_policy /*policy*/,
3131
handle /*parent*/) {
3232
py::object tv_py = py::module::import("cudaq").attr("SpinOperator")(
33-
v.getDataRepresentation(), v.num_qubits()); // Construct new python obj
33+
v.get_data_representation()); // Construct new python obj
3434
return tv_py.release();
3535
}
3636
};

libs/qec/unittests/backend-specific/stim/test_qec_stim.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
TEST(QECCodeTester, checkRepetitionNoiseStim) {
1818

19-
auto repetition = cudaq::qec::get_code("repetition", {{"distance", 9}});
19+
auto repetition = cudaq::qec::get_code(
20+
"repetition", cudaqx::heterogeneous_map{{"distance", 9}});
2021
{
2122
cudaq::set_random_seed(13);
2223
cudaq::noise_model noise;

libs/qec/unittests/test_qec.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 NVIDIA Corporation & Affiliates. *
2+
* Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. *
33
* All rights reserved. *
44
* *
55
* This source code and the accompanying materials are made available under *
@@ -420,14 +420,15 @@ TEST(QECCodeTester, checkRepetition) {
420420
// must provide distance
421421
EXPECT_THROW(cudaq::qec::get_code("repetition"), std::runtime_error);
422422
}
423-
auto repetition = cudaq::qec::get_code("repetition", {{"distance", 9}});
423+
auto repetition = cudaq::qec::get_code(
424+
"repetition", cudaqx::heterogeneous_map{{"distance", 9}});
424425

425426
{
426427
auto stabilizers = repetition->get_stabilizers();
427428

428429
std::vector<std::string> actual_stabs;
429430
for (auto &s : stabilizers)
430-
actual_stabs.push_back(s.to_string(false));
431+
actual_stabs.push_back(s.begin()->get_pauli_word());
431432

432433
std::vector<std::string> expected_strings = {
433434
"ZZIIIIIII", "IZZIIIIII", "IIZZIIIII", "IIIZZIIII",
@@ -512,7 +513,8 @@ TEST(QECCodeTester, checkSurfaceCode) {
512513
}
513514
{
514515
// with default stabilizers
515-
auto surf_code = cudaq::qec::get_code("surface_code", {{"distance", 3}});
516+
auto surf_code = cudaq::qec::get_code(
517+
"surface_code", cudaqx::heterogeneous_map{{"distance", 3}});
516518
cudaqx::tensor<uint8_t> parity = surf_code->get_parity();
517519
cudaqx::tensor<uint8_t> parity_x = surf_code->get_parity_x();
518520
cudaqx::tensor<uint8_t> parity_z = surf_code->get_parity_z();
@@ -668,7 +670,8 @@ TEST(QECCodeTester, checkSteaneSPAM) {
668670

669671
TEST(QECCodeTester, checkRepetitionSPAM) {
670672
// only Z basis for repetition
671-
auto repetition = cudaq::qec::get_code("repetition", {{"distance", 9}});
673+
auto repetition = cudaq::qec::get_code(
674+
"repetition", cudaqx::heterogeneous_map{{"distance", 9}});
672675
EXPECT_TRUE(noiseless_logical_SPAM_test(*repetition,
673676
cudaq::qec::operation::prep0, 0));
674677
EXPECT_TRUE(noiseless_logical_SPAM_test(*repetition,
@@ -681,7 +684,8 @@ TEST(QECCodeTester, checkRepetitionSPAM) {
681684

682685
TEST(QECCodeTester, checkSurfaceCodeSPAM) {
683686
// Must compile with stim for larger distances
684-
auto surf_code = cudaq::qec::get_code("surface_code", {{"distance", 3}});
687+
auto surf_code = cudaq::qec::get_code(
688+
"surface_code", cudaqx::heterogeneous_map{{"distance", 3}});
685689
EXPECT_TRUE(
686690
noiseless_logical_SPAM_test(*surf_code, cudaq::qec::operation::prep0, 0));
687691
EXPECT_TRUE(

libs/solvers/lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ target_link_libraries(cudaq-solvers
4646
PUBLIC
4747
cudaqx-core
4848
cudaq::cudaq
49-
cudaq::cudaq-spin
49+
cudaq::cudaq-operator
5050
PRIVATE
5151
cudaq::cudaq-common
5252
nlohmann_json::nlohmann_json

0 commit comments

Comments
 (0)