Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Conversation

@swernli
Copy link
Collaborator

@swernli swernli commented Jun 4, 2021

This change adds a new utility to the full state simulator, is_classical_or_entangled that returns a pair of bools indicating when the given qubit is classical with regard to the computational basis and whether the qubit is entangled with any other qubits. The former is needed to ensure the state of the qubit is collapsed before release, while the latter is the correct feedback to return from release to indicate whether releasing that qubit had any side effects on other qubits in the wave function.

This updates the logic in the C# infrastructure to use the returned bool as the indication of whether or not the release was valid (as opposed to the old check that used a cache of whether or not the last operation on the qubit was a measurement). THIS IS A CHANGE IN BEHAVIOR. Notably, a qubit is now considered valid for release as long as it is not entangled with any other qubit, regardless of superposition. As a result, the exception name and text for ReleasedQubitsAreNotInZeroState is changed to ReleaseQubitsAreEntangled.

This also fixes #552 by having the FullstateSimulator wrapper in the QIR Runtime check the return value from release and call quantum__rt__fail_cstr if the qubit released was entangled.

This change adds a new utility to the full state simulator, `is_classical_or_entangled` that returns a pair of bools indicating when the given qubit is classical with regard to the computational basis and whether the qubit is entangled with any other qubits. The former is needed to ensure the state of the qubit is collapsed before release, while the latter is the correct feedback to return from release to indicate whether releasing that qubit had any side effects on other qubits in the wave function.

This updates the logic in the C# infrastructure to use the returned bool as the indication of whether or not the release was valid (as opposed to the old check that used a cache of whether or not the last operation on the qubit was a measurement). THIS IS A CHANGE IN BEHAVIOR. Notably, a qubit that is now considered valid for release as long as it is not entangled with any other qubit, regardless of superposition. As a result, the exception name and text for `ReleasedQubitsAreNotInZeroState` is changed to `ReleaseQubitsAreEntangled`.

This also fixes QIR simulator should check result of qubit Release #552 by having the FullstateSimulator wrapper in the QIR Runtime check the return value from release and call `quantum__rt__fail_cstr` if the qubit released was entangled.
Copy link
Contributor

@dbwz8 dbwz8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to tell if the logic for entanglement is correct by inspection... but since the tests you wrote should catch any of the errors I can think of, I see no reason to block the change.

@swernli
Copy link
Collaborator Author

swernli commented Jun 4, 2021

Worth noting with this change: because it asserts that any qubit that is not entangled is valid for release, it assumes that on hardware qubits are either explicitly or implicitly reset on release OR every qubit is explicitly reset on allocate. This would be needed to match the Q# assertion that every freshly allocated qubit shows up in the ground state. While this matches behavior for our currently supported hardware partners, it's worth underscoring in case anyone sees that as a problematic assumption going forward.

@swernli
Copy link
Collaborator Author

swernli commented Jun 5, 2021

Looks like ReleasedQubitsAreNotInZeroState is used directly in samples, so this will require a cross-repo change.

@swernli
Copy link
Collaborator Author

swernli commented Jun 5, 2021

On second thought, looking at how samples uses the exception, I can simply leave the existing type with a deprecation. I'll go that route instead.

@swernli
Copy link
Collaborator Author

swernli commented Jun 5, 2021

@dbwz8 It turns out the entanglement check can be further simplified! Because of the way we are iterating over the wave function, we are always checking the two states where the only difference is the qubit in question. If both of those have non-zero probability, that means there is a chance of measuring the identical values for every other qubit regardless of the state of the qubit we care about, which by definition means it cannot be entangled with any of the other qubits. If we never see that case, then it must mean that every measurement of the qubit we care about corresponds to a specific measurement of at least one other qubit. The check I was doing before (comparing the two indices for differences other than the target qubit) was redundant, because in the case where the probabilities were non-zero (the case we care about) that check would always be true.

@swernli
Copy link
Collaborator Author

swernli commented Jun 6, 2021

@kuzminrobin thanks for the review and feedback! Especially the observation about the use of mutex() which could possibly be the resolution to our long standing intermediate access violation in CI. Since this PR includes some changes in behavior for the simulator that affect the observed behavior for users of Q#, I'll hold off on merging until a few more stakeholders weigh in.
@bettinaheim, @cgranade, @tcNickolas, any opinions on that part?

@cgranade
Copy link
Contributor

cgranade commented Jun 6, 2021

@kuzminrobin thanks for the review and feedback! Especially the observation about the use of mutex() which could possibly be the resolution to our long standing intermediate access violation in CI. Since this PR includes some changes in behavior for the simulator that affect the observed behavior for users of Q#, I'll hold off on merging until a few more stakeholders weigh in.
@bettinaheim, @cgranade, @tcNickolas, any opinions on that part?

Sorry for the delay, been a bit head down trying to fix the e2e build for #709, and got behind on this one. Anyway, I'm in the awkward position of thinking that both the old and the new behavior makes sense... effectively, my thinking comes back to that when a user allocates a qubit with use, we currently document that they're promising that they will either measure before deallocating or will coherently return it to |0⟩. That promise can't be checked on real hardware, so running on simulators with checks like these is helpful to ensure that condition is met. From that perspective, checking that a released qubit is in |0⟩ is a much stronger guarantee than that it is separable, such that allowing users to pick a weaker condition can be helpful when running against targets that implicitly reset on deallocation (that is, where coherent return to |0⟩ isn't allowed). In other cases, though, the old behavior of checking for |0⟩ makes sense, such that if possible, I feel like making it configurable would be really helpful?

Apologies for the incoherent (pun fully intended) wall of text, just my 2¢ from an initial pass.

@swernli
Copy link
Collaborator Author

swernli commented Jun 6, 2021

No worries at all, @cgranade, I definitely wasn't expecting a response over the weekend!

From that perspective, checking that a released qubit is in |0⟩ is a much stronger guarantee than that it is separable, such that allowing users to pick a weaker condition can be helpful when running against targets that implicitly reset on deallocation (that is, where coherent return to |0⟩ isn't allowed). In other cases, though, the old behavior of checking for |0⟩ makes sense, such that if possible, I feel like making it configurable would be really helpful?

Making it configurable is a really interesting idea, and stands to reason that would allow the simulation to meet the needs of a broader audience. In fact, the C# simulator already has a setting that can disable this check entirely, which QIR simulation infrastructure does not replicate. Just thinking out loud here, the measurement tracking felt like it was a stand in for entanglement (we know a measured qubit couldn't be entangled) but was already weaker than the zero check. If we have release in the full state simulator return a pair of bools, one indicating if the qubit was in the ground state and one indicating if it was entangled, then we could pretty easily have three configurable settings: allow release of any qubit, fail on release of entangled qubits, fail on release of non-zero qubits. Then both C# and QIR could offer the same functionality with minimal additional overhead (no need for measurement frame tracking). We'd have to decide which of those three is the default and how we'd want to expose the setting for QIR (environment variable perhaps?), but it would not be hard to implement given the information the new is_classical_or_entangled function returns.

@swernli swernli marked this pull request as draft July 12, 2021 06:46
@swernli
Copy link
Collaborator Author

swernli commented Jul 12, 2021

I've converted this to a draft while we work out the questions @cgranade brought up about configurability. That said, #552 should be considered a critical issue for QIR-based simulation, so we should aim to have this included in the next release if at all possible. @bettinaheim @cesarzc FYI.

@swernli
Copy link
Collaborator Author

swernli commented Jul 21, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@swernli
Copy link
Collaborator Author

swernli commented Jul 22, 2021

@cgranade Regarding configuration for simulation, there is still an outstanding general question about how we want to support configuring QDK, ranging from settings in the qubit manager to this qubit release behavior and even noise models for the experimental open simulator. I believe both @DmitryVasilevsky and @kuzminrobin have started looking into this and might want to weigh in for this part.

Copy link
Contributor

@bettinaheim bettinaheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change makes sense. I did look at the test cases and think they cover everything.


namespace Microsoft.Quantum.Simulation.Simulators.Exceptions
{
[Obsolete("This class is deprecated and will be removed in a future release.")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to refer to the ReleasedQubitsAreEntangled exception?

return true;
}

template <class T, class A>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A doc comment here would be helpful. The naming "is classical" is a bit confusing to me, since aside from in the simulator, I don't think the choice of basis should matter. It being "in computational basis" seems more accurate to me, but then that would be awfully long as name...

@bettinaheim
Copy link
Contributor

@cgranade
"... my thinking comes back to that when a user allocates a qubit with use, we currently document that they're promising that they will either measure before deallocating or will coherently return it to |0⟩. [...], checking that a released qubit is in |0⟩ is a much stronger guarantee than that it is separable, such that allowing users to pick a weaker condition can be helpful when running against targets that implicitly reset on deallocation (that is, where coherent return to |0⟩ isn't allowed). In other cases, though, the old behavior of checking for |0⟩ makes sense, such that if possible, I feel like making it configurable would be really helpful?"

What is not clear to me is the value of requiring that a qubit is measured (i.e. in the 0,1 basis) rather than in any unentangled state. The whole idea of these checks was that it is completely fine to measure a qubit after it is release (so it can be reused) without that changing the algorithm. What am I missing - why would the stricter condition be beneficial?

@bettinaheim
Copy link
Contributor

After some offline discussion, the concern is captured below.
I don't really see a good way to determine when a qubit is coherently reset to zero if qubits can be released in an arbitrary state as long as they are unentangled. What is outlined below is actually a pretty important factor that I didn't think of in the rush of the moment yesterday. @swernli Thoughts?

cgranade:
When qubits are released, the target resets them to |0⟩ automatically before they are allocated again? In that case, how does the target differentiate between automatic reset behaviour and the coherent unpreparation case where a developer has already reset to |0⟩ before releasing?
At the moment, the demand that a qubit is either in |0⟩ or is measured individually allows for both cases to be covered without introducing measurements in the coherent unpreparation case; weakening to "reset if unentangled" means that since a target cannot in general prove they're unentangled, the target needs to always reset.
Could imagine a weakening where the target can prove exactly what state the qubits being released are in from whatever measurements have been performed (e.g.: let r1 = Measure([PauliZ, PauliZ], qs); let r2 = Measure([PauliX, PauliX], qs); is enough to nail down exactly which state qs is in and to thus reset w/o additional measurements), but weakening all the way to unentangled seems to me like it'd necessarily introduce extra measurements in the unpreparation case."

bettinaheim:
"Hm, I can see where you are coming from. I'd like to avoid opt in or opt out for a more strict check here, since otherwise we basically need to propagate with what setting the program has been compiled with to the code generation stage to avoid exactly that. That seem rather ad-hoc. On the other hand, a good way might be that releases are annotated by the compiler with a flag indicating that there is no need to reset the qubit when the more strict checking is turned on. That could make sense.

cgranade:
Right. It's at the least a change in behavior that would change how libraries are optimized (for example, wouldn't want to coherently unprepare unless the compiler knows either automatically or via a flag that those qubits don't then need to be reset).

@swernli
Copy link
Collaborator Author

swernli commented Aug 4, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

swernli added a commit that referenced this pull request Aug 13, 2021
This mimicks the same behavior in the QIR Runtime wrapper for the fullstate simulator as what we have for C#, namely that a qubit is falid for release if and only if it is either in the ground state or the last operation on that qubit was measure.

This fixes #552, and supercedes #710.
@swernli
Copy link
Collaborator Author

swernli commented Aug 13, 2021

This PR is superseded by #796

@swernli swernli closed this Aug 13, 2021
swernli added a commit that referenced this pull request Aug 17, 2021
This change fixes what seems to be a subtle bug in the syncrhonization mechanisms for the simulator. In particular, we have seen intermediate failures in the simulator tests that seem to be the result of a simulator thread stepping on another thread. As @kuzminrobin pointed out in #710 (comment), this could be due to the fact that the mutex access method, `mutex()` has the same signature as the standard constructor for mutex. This could mean that if the compilation environment has a `using namespace std;` anwhere in it that the calls to `mutex()` would allocate a fresh mutex rather than access the member mutex, meaning none of the threads are synchronized as expected. This avoid the problem by renaming the accessor to `getmutex()`.
swernli added a commit that referenced this pull request Aug 17, 2021
* Fixing use of `mutex()` to avoid subtle bug

This change fixes what seems to be a subtle bug in the syncrhonization mechanisms for the simulator. In particular, we have seen intermediate failures in the simulator tests that seem to be the result of a simulator thread stepping on another thread. As @kuzminrobin pointed out in #710 (comment), this could be due to the fact that the mutex access method, `mutex()` has the same signature as the standard constructor for mutex. This could mean that if the compilation environment has a `using namespace std;` anwhere in it that the calls to `mutex()` would allocate a fresh mutex rather than access the member mutex, meaning none of the threads are synchronized as expected. This avoid the problem by renaming the accessor to `getmutex()`.

* Update calls in macro
swernli added a commit that referenced this pull request Aug 17, 2021
* Check qubit release/measurement status on release

This mimics the same behavior in the QIR Runtime wrapper for the fullstate simulator as what we have for C#, namely that a qubit is valid for release if and only if it is either in the ground state or the last operation on that qubit was measure.

This fixes #552, and supersedes #710.

* CR feedback

* Revert mutex change
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

QIR simulator should check result of qubit Release

7 participants