Commit a5071d5f authored by Nguyen, Thien's avatar Nguyen, Thien
Browse files

Optimize the all commute check for Pauli



iterate the list of terms once and use a more efficient way to check the commutation.

Add more test cases.
Signed-off-by: Nguyen, Thien's avatarThien Nguyen <nguyentm@ornl.gov>
parent 4eeccedc
......@@ -304,23 +304,35 @@ PauliOperator::observe(std::shared_ptr<CompositeInstruction> function, const Het
// For this grouping, we only support *single* grouping,
// i.e. all sub-terms commute.
const int nbQubits = std::max<int>(function->nPhysicalBits(), nQubits());
const bool all_terms_commute = [this]() {
auto terms = getNonIdentitySubTerms();
for (int i = 0; i < terms.size(); ++i) {
auto this_term =
std::dynamic_pointer_cast<quantum::PauliOperator>(terms[i]);
assert(this_term);
for (int j = i + 1; j < terms.size(); ++j) {
auto other_term =
std::dynamic_pointer_cast<quantum::PauliOperator>(terms[j]);
assert(other_term);
// TODO: has a fast path for all-Z, all-X, all-Y
// so that we don't need to check commutation.
if (!this_term->commutes(*other_term)) {
return false;
const bool all_terms_commute = [this, nbQubits]() {
// Check that each qubit location has a **unique** basis
// across all terms:
// This is much faster than checking the commutes()
std::unordered_map<int, int> qubit_to_basis;
for (auto &inst : terms) {
Term spinInst = inst.second;
std::vector<int> meas_bits;
if (!spinInst.isIdentity()) {
auto [v, w] = spinInst.toBinaryVector(nbQubits);
assert(v.size() == w.size());
for (int i = 0; i < v.size(); ++i) {
if (v[i] != 0 || w[i] != 0) {
// 1, 2, 3 => X, Z, Y
int op_id = v[i] + 2 * w[i];
if (qubit_to_basis.find(i) == qubit_to_basis.end()) {
// Not seen this:
qubit_to_basis[i] = op_id;
} else {
if (qubit_to_basis[i] != op_id) {
// Different basis at this location.
return false;
}
}
}
}
}
}
// unique basis at each qubit location.
return true;
}();
......
......@@ -490,27 +490,59 @@ TEST(PauliOperatorTester, checkNormalize) {
}
TEST(PauliOperatorTester, checkGroupingQaoa) {
PauliOperator op;
op.fromString("(0, -1) Z0 Z1 + (0, -1) Z1 Z2 + (0, -1) Z2 Z0");
std::cout << op.toString() << "\n";
auto qpu = xacc::getAccelerator("qpp", {{"shots", 1024}});
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto f = gateRegistry->createComposite("f");
auto h0 = gateRegistry->createInstruction("H", 0);
auto h1 = gateRegistry->createInstruction("H", 1);
auto h2 = gateRegistry->createInstruction("H", 2);
f->addInstructions({h0, h1, h2});
auto observed = op.observe(f, {{"accelerator", qpu}});
EXPECT_EQ(observed.size(), 1);
TEST(PauliOperatorTester, checkGroupingCommuteCheck) {
PauliOperator op0;
op0.fromString("(0, -1) Z0 Z1 + (0, -1) Z1 Z2 + (0, -1) Z2 Z0");
std::cout << op0.toString() << "\n";
PauliOperator op1;
op1.fromString("(0, -1) Z0 + (0, -1) Z1 + (0, -1) X2");
std::cout << op1.toString() << "\n";
PauliOperator op2;
op2.fromString("(0, -1) Z0 Z1 + (0, -1) Z1 Z2 + (0, -1) X3 Y4");
std::cout << op2.toString() << "\n";
PauliOperator op3;
op3.fromString("(0, -1) Z0 X1 + (0, -1) X1 Y2 + (0, -1) Y2 Z3");
std::cout << op3.toString() << "\n";
// No optimization allowed here:
PauliOperator op4_no_opt_1;
op4_no_opt_1.fromString(
"(0, -1) Z0 X1 + (0, -1) X1 Y2 + (0, -1) Y2 Z3 + (0, -1) X0");
std::cout << op4_no_opt_1.toString() << "\n";
PauliOperator op4_no_opt_2;
op4_no_opt_2.fromString("(0, -1) Z0 Z1 + (0, -1) Z1 Z2 + (0, -1) Z2 X0");
std::cout << op4_no_opt_2.toString() << "\n";
std::vector<PauliOperator> opt_cases{op0, op1, op2, op3};
for (auto &test_case : opt_cases) {
auto qpu = xacc::getAccelerator("qpp", {{"shots", 1024}});
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto f = gateRegistry->createComposite("f");
auto h0 = gateRegistry->createInstruction("H", 0);
auto h1 = gateRegistry->createInstruction("H", 1);
auto h2 = gateRegistry->createInstruction("H", 2);
f->addInstructions({h0, h1, h2});
auto observed = test_case.observe(f, {{"accelerator", qpu}});
EXPECT_EQ(observed.size(), 1);
}
// Check non-commute as well:
PauliOperator op_non_commute;
op_non_commute.fromString("(0, -1) Z0 Z1 + (0, -1) Z1 Z2 + (0, -1) Z2 X0");
std::cout << op_non_commute.toString() << "\n";
auto observed_non_commute = op_non_commute.observe(f, {{"accelerator", qpu}});
// Three terms:
EXPECT_EQ(observed_non_commute.size(), 3);
std::vector<PauliOperator> no_opt_cases{op4_no_opt_1, op4_no_opt_2};
for (auto &test_case : no_opt_cases) {
auto qpu = xacc::getAccelerator("qpp", {{"shots", 1024}});
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto f = gateRegistry->createComposite("f");
auto h0 = gateRegistry->createInstruction("H", 0);
auto h1 = gateRegistry->createInstruction("H", 1);
auto h2 = gateRegistry->createInstruction("H", 2);
f->addInstructions({h0, h1, h2});
auto observed = test_case.observe(f, {{"accelerator", qpu}});
EXPECT_EQ(observed.size(), test_case.getNonIdentitySubTerms().size());
}
}
TEST(PauliOperatorTester, checkGroupingQaoaPostProcessLSB) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment