Closed tobiashagge closed 1 year ago
I have verified that adding ResetAll(qubits) at the end of AssertEqualOnBasisVector seems to address the behavior. Not sure whether the absence of the added line is the bug or if some tolerance code is not behaving to spec.
Thanks for the detailed issue, @tobiashagge! There are a few points to go over in this and I will get into more detail below, but the summary is that your suggestion of this being an issue with the tolerance in AssertEqualOnBasisVector
is correct. I’ll work on a PR to resolve this.
Regarding your first point in your expected behavior above:
When the theta = .1, an exception, but one that does not claim that the expected value should be "Zero".
This is actually the expected error message, and is consistent with the behavior of our other assert APIs. Most of the different assert operations are built from calls to these two operations: https://github.com/microsoft/qsharp-runtime/blob/a86051802eca2e96b270255e2f42b580b43c8945/src/Simulation/QSharpFoundation/Diagnostics/AssertQubit.qs#L27-L29 And https://github.com/microsoft/qsharp-runtime/blob/a86051802eca2e96b270255e2f42b580b43c8945/src/Simulation/QSharpFoundation/Diagnostics/AssertQubit.qs#L55-L57 The other asserts use combinations of gate operations, entanglement, and calls to these two functions to enforce the assertion. Some target qubit or auxiliary qubit deviating from the expected state is the trigger for the assertion, and that’s why you see the message they use.
For your second point regarding the difference in behavior between the two angles in your example,
When theta=.001, the program should complete without throwing an exception.
Here is where the problem with tolerance comes in. Because the AssertOperationsEqualInPlace
does not have a tolerance parameter and instead uses a hard-coded value, it should use the least permissive tolerance possible to adequately enforce the assertion. The value used in this case is 1e-5
which is definitely too permissive. Picking the perfect value is tricky given that floating point precision can vary by platform and standard library (see for example C numeric limits and Machine Epsilon). The underlying C++ full state simulator that checks for qubits in the Zero state on release has an epsilon of approx 2e-16 on Windows. Looking through our codebase, there are a few other places where we rely on an epsilon/tolerance, and it seems the best value to standardize behind is 1e-15
since that’s what the Q# implementation of Double
conversions uses:
https://github.com/microsoft/qsharp-runtime/blob/a86051802eca2e96b270255e2f42b580b43c8945/src/Simulation/QSharpFoundation/Math/Math.qs#L532-L539
Combining this with your suggestion of including a ResetAll
before the qubits are released will bring the behavior of AssertOperationsEqualInPlace
in line with our other assertion utilities.
On that note of other assertion utilities, if your goal is to have an efficient validation that two unitaries are equivalent for all input states, I would suggest switching to AssertOperationsEqualReferenced instead. As noted in the docs, AssertOperationsEqualInPlace
requires both additional qubits and multiple calls to the provided unitaries, while AssertOperationsEqualReferenced
uses Choi–Jamiołkowski isomorphism to perform the same validation with only one call to the underlying unitaries. Using the examples you posted above, AssertOperationsEqualReferenced
correctly asserts on both inputs and runs faster. This is the same strategy we use in our test cases to validate equivalence of different decompositions of quantum gates, and is quite handy.
Hmm… looks like 1e-15
is a reasonable value for the full state simulator, but fails the tests for the stabilizer simulator based on the noise levels. To make these tests still pass, I’ll use the same tolerance used by the other tests:
https://github.com/microsoft/qsharp-runtime/blob/087e20ddf05e79d0bd483c5802022c7780d493b2/src/Simulation/Simulators.Tests/QuantumTestSuite/Config.qs#L18-L21
That’s the value that gets passed for other test operations that call AssertQubitInStateWithinTolerance
.
Describe the bug
AssertOperationsEqualInPlace throws exceptions claiming that released qubits aren't in a zero state when two operations are approximately, but not exactly, equal. Since the qubits are released by library code there is no way to prevent the exception. Also, when operations are different, there is a bug in the text for the exception thrown.
To Reproduce
Expected behavior
When the theta = .1, an exception, but one that does not claim that the expected value should be "Zero".
When theta=.001, the program should complete without throwing an exception.
Actual behavior
For theta = .1:
For theta = .001:
System information
OS: Windows 10
.NET Core Version: 6.0.405
IQ# Version: iqsharp: 0.26.233415 Jupyter Core: 3.0.0.0 .NET Runtime: .NETCoreApp,Version=v6.0
Additional context
I suspect the issue is in Simulation/TargetDefinitions/Decompositions/AssertOperationsEqualInPlace.qs, at the end of operation AssertEqualOnBasisVector. ResetAll is never performed on the qubits and I suspect that a tolerance mismatch allows the exception to be thrown.