Skip to content

🐛 Numerical instability in DD package for large-scale systems #575

Open
@burgholzer

Description

@burgholzer

Environment information

Dates back to the very origin of the (QM)DD package.

Description

The code down below demonstrates an instability in the matrix DD normalization.

The script just creates a bunch of Hadamards and then reverses them.
Up until (including) 81 qubits, this produces results as expected. The result is equal to the identity and the top edge weight is one.
For n=81+k, the top edge weight will be sqrt(2)^k ... Meaning that, for example, at n=128 the top edge weight is equal to roughly a million 😅
The main reason for this is that the first bunch of H's generates a DD that has a top edge weight of (1/sqrt(2))^n until n hits 81 where the default numerical tolerance kicks in and the value remains constant at 6.4311e-13, which is just above the default tolerance. Now each qubit above 81 in the second series of H's induces an error of sqrt(2).
In extreme cases, this may even overflow the range of double. Particularly, for n>=2129, sqrt(2)^(n-81) overflows the largest possible double precision floating point value (1.7976931348623158e+308).

Possible consequences of the above include

  • NaN values after overflows
  • DDs collapsing to the all-zero DD (because the top-weight underflows the tolerance) and subsequent segfaults
  • Performance problems due to polluting the 1.0 unique table bucket.

Expected behavior

Matrix normalization should be stable and not induce these kinds of errors.

This will most surely require the development of a new normalization scheme for matrix DDs (probably inspired by the one currently used for vectors), which, from past experience, might not be that easy to get right.

How to Reproduce

Run the following test

TEST(DDPackageTest, MatrixNormalizationRegression) {
  const qc::Qubit nqubits = 100U;
  for (auto q = 1U; q <= nqubits; ++q) {
    auto dd = std::make_unique<dd::Package<>>(q);
    auto f = dd->makeIdent();
    for (qc::Qubit i = 0; i < q; ++i) {
      const auto h = dd->makeGateDD(dd::H_MAT, i);
      f = dd->multiply(f, h);
    }
    for (qc::Qubit i = 0; i < q; ++i) {
      const auto h = dd->makeGateDD(dd::H_MAT, q - i - 1);
      f = dd->multiply(f, h);
    }
    std::cout << "Qubits: " << q << ", ";
    std::cout << "Top edge weight: " << f.w.toString(false) << ", ";
    std::cout << "Is identity: " << f.isIdentity(false) << "\n";
    const auto w = static_cast<dd::ComplexValue>(f.w);
    EXPECT_TRUE(w.approximatelyEquals({1., 0.}));
    EXPECT_TRUE(f.isIdentity(false));
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    DDAnything related to the DD packagebugSomething isn't workingc++Anything related to C++ codehelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions