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 *
88#include " cudaq/qec/stabilizer_utils.h"
99
1010namespace cudaq ::qec {
11+ // Sort the stabilizers by the occurrence of 'Z' first, then by 'X'.
12+ // An earlier occurrence is considered "less".
13+ // 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 );
17+ auto zIdxA = astr.find_first_of (" Z" );
18+ auto zIdxB = bstr.find_first_of (" Z" );
19+ if (zIdxA == std::string::npos) {
20+ if (zIdxB != std::string::npos)
21+ return false ;
22+
23+ // No Z in either, must both contain a X
24+ auto xIdxA = astr.find_first_of (" X" );
25+ return xIdxA < bstr.find_first_of (" X" );
26+ }
27+
28+ // First contains a Z
29+ if (zIdxB == std::string::npos)
30+ return true ;
31+
32+ return zIdxA < zIdxB;
33+ }
34+
35+ static bool isStabilizerSorted (const std::vector<cudaq::spin_op> &ops) {
36+ return std::is_sorted (ops.begin (), ops.end (), spinOpComparator);
37+ }
38+
1139void sortStabilizerOps (std::vector<cudaq::spin_op> &ops) {
12- // Sort the stabilizers, z first, then x
13- std::sort (ops.begin (), ops.end (),
14- [](const cudaq::spin_op &a, const cudaq::spin_op &b) {
15- auto astr = a.to_string (false );
16- auto bstr = b.to_string (false );
17- auto zIdxA = astr.find_first_of (" Z" );
18- auto zIdxB = bstr.find_first_of (" Z" );
19- if (zIdxA == std::string::npos) {
20- if (zIdxB != std::string::npos)
21- return false ;
22-
23- // No Z in either, must both contain a X
24- auto xIdxA = astr.find_first_of (" X" );
25- return xIdxA < bstr.find_first_of (" X" );
26- }
27-
28- // First contains a Z
29- if (zIdxB == std::string::npos)
30- return true ;
31-
32- return zIdxA < zIdxB;
33- });
40+ std::sort (ops.begin (), ops.end (), spinOpComparator);
3441}
3542
3643// Need to push into the form
@@ -42,14 +49,25 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
4249 if (stabilizers.empty ())
4350 return cudaqx::tensor<uint8_t >();
4451
45- sortStabilizerOps (const_cast <std::vector<cudaq::spin_op> &>(stabilizers));
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+ }
4664
4765 if (type == stabilizer_type::XZ) {
48- auto numQubits = stabilizers [0 ].num_qubits ();
49- cudaqx::tensor<uint8_t > t ({stabilizers. size (), 2 * numQubits});
66+ auto numQubits = (*p_stabilizers) [0 ].num_qubits ();
67+ cudaqx::tensor<uint8_t > t ({p_stabilizers-> size (), 2 * numQubits});
5068 // Start by counting the number of Z spin_ops
5169 std::size_t numZRows = 0 ;
52- for (auto &op : stabilizers )
70+ for (auto &op : *p_stabilizers )
5371 if (op.to_string (false ).find (" Z" ) != std::string::npos)
5472 numZRows++;
5573 else
@@ -58,16 +76,16 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
5876 // Need to shift Z bits left
5977 for (std::size_t row = 0 ; row < numZRows; row++) {
6078 for (std::size_t i = numQubits; i < 2 * numQubits; i++) {
61- if (stabilizers [row].get_raw_data ().first [0 ][i])
79+ if ((*p_stabilizers) [row].get_raw_data ().first [0 ][i])
6280 t.at ({row, i - numQubits}) = 1 ;
6381 }
6482 }
6583
66- auto numXRows = stabilizers. size () - numZRows;
84+ auto numXRows = p_stabilizers-> size () - numZRows;
6785
6886 for (std::size_t row = 0 ; row < numXRows; row++) {
6987 for (std::size_t i = 0 ; i < numQubits; i++) {
70- if (stabilizers [numZRows + row].get_raw_data ().first [0 ][i])
88+ if ((*p_stabilizers) [numZRows + row].get_raw_data ().first [0 ][i])
7189 t.at ({numZRows + row, i + numQubits}) = 1 ;
7290 }
7391 }
@@ -76,10 +94,10 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
7694 }
7795
7896 if (type == stabilizer_type::Z) {
79- auto numQubits = stabilizers [0 ].num_qubits ();
97+ auto numQubits = (*p_stabilizers) [0 ].num_qubits ();
8098 // Start by counting the number of Z spin_ops
8199 std::size_t numZRows = 0 ;
82- for (auto &op : stabilizers )
100+ for (auto &op : *p_stabilizers )
83101 if (op.to_string (false ).find (" Z" ) != std::string::npos)
84102 numZRows++;
85103 else
@@ -91,32 +109,32 @@ to_parity_matrix(const std::vector<cudaq::spin_op> &stabilizers,
91109 cudaqx::tensor<uint8_t > ret ({numZRows, numQubits});
92110 for (std::size_t row = 0 ; row < numZRows; row++) {
93111 for (std::size_t i = numQubits; i < 2 * numQubits; i++) {
94- if (stabilizers [row].get_raw_data ().first [0 ][i])
112+ if ((*p_stabilizers) [row].get_raw_data ().first [0 ][i])
95113 ret.at ({row, i - numQubits}) = 1 ;
96114 }
97115 }
98116
99117 return ret;
100118 }
101119
102- auto numQubits = stabilizers [0 ].num_qubits ();
120+ auto numQubits = (*p_stabilizers) [0 ].num_qubits ();
103121 // Start by counting the number of Z spin_ops
104122 std::size_t numZRows = 0 ;
105- for (auto &op : stabilizers )
123+ for (auto &op : *p_stabilizers )
106124 if (op.to_string (false ).find (" Z" ) != std::string::npos)
107125 numZRows++;
108126 else
109127 break ;
110128
111- auto numXRows = stabilizers. size () - numZRows;
129+ auto numXRows = p_stabilizers-> size () - numZRows;
112130
113131 if (numXRows == 0 )
114132 return cudaqx::tensor<uint8_t >();
115133
116134 cudaqx::tensor<uint8_t > ret ({numXRows, numQubits});
117135 for (std::size_t row = 0 ; row < numXRows; row++) {
118136 for (std::size_t i = 0 ; i < numQubits; i++) {
119- if (stabilizers [numZRows + row].get_raw_data ().first [0 ][i])
137+ if ((*p_stabilizers) [numZRows + row].get_raw_data ().first [0 ][i])
120138 ret.at ({row, i}) = 1 ;
121139 }
122140 }
@@ -130,6 +148,7 @@ cudaqx::tensor<uint8_t> to_parity_matrix(const std::vector<std::string> &words,
130148 std::vector<cudaq::spin_op> ops;
131149 for (auto &os : words)
132150 ops.emplace_back (cudaq::spin_op::from_word (os));
151+ sortStabilizerOps (ops);
133152 return to_parity_matrix (ops, type);
134153}
135154} // namespace cudaq::qec
0 commit comments