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

Conversation

@IrinaYatsenko
Copy link
Contributor

@IrinaYatsenko IrinaYatsenko commented Nov 9, 2020

Added new non-Adj/non-Ctr operation _PrepareAmplitudesFromZeroState that will emulate state preparation if running on a simulator that supports it and the prerequisites are met.

Had to move onto SDK 0.14.20111301-pull to pick up the new functionality in the Quantum Simulator.

@IrinaYatsenko
Copy link
Contributor Author

IrinaYatsenko commented Nov 9, 2020

It looks like TrotterEstimateEnergy in https://github.com/microsoft/QuantumLibraries/blob/main/Chemistry/tests/JupyterTests/sample.py is attempting to prepare state on qubits that aren't in state |0...0>.

@cgranade:

  1. Documentation for PrepareArbitraryState says "Qubit register encoding number states in little-endian format. This is expected to be initialized in the computational basis state |0...0⟩.". I had interpreted "expected" as a hard prerequisite and made Quantum Simulator throw, when the condition is violated. However, it seems that the current implementation of the API doesn't fail on non-zero qubits -- is it a bug or "expected" means that we simply don't guarantee a meaningful result if the target qubits aren't in zero state?

  2. If throwing is the right thing to do, who owns this test and could help me to fix it to satisfy the prerequisites?

Copy link
Contributor

@cgranade cgranade left a comment

Choose a reason for hiding this comment

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

Not had a chance to do a complete review yet, sorry; please treat these comments as provisional for now. Thanks for your patience!

@cgranade
Copy link
Contributor

It looks like TrotterEstimateEnergy in https://github.com/microsoft/QuantumLibraries/blob/main/Chemistry/tests/JupyterTests/sample.py is attempting to prepare state on qubits that aren't in state |0...0>.

@cgranade:

  1. Documentation for PrepareArbitraryState says "Qubit register encoding number states in little-endian format. This is expected to be initialized in the computational basis state |0...0⟩.". I had interpreted "expected" as a hard prerequisite and made Quantum Simulator throw, when the condition is violated. However, it seems that the current implementation of the API doesn't fail on non-zero qubits -- is it a bug or "expected" means that we simply don't guarantee a meaningful result if the target qubits aren't in zero state?

Since PrepareAribtraryState supports is Adj + Ctl, it has to do satisfy the invariant that PrepareArbitraryState(input); Adjoint PrepareArbitraryState(input); is a no-op even if the qubits in input are not in the |00…0⟩ state initially. From that perspective, the verb "Prepare" is used to communicate that the action of the operation is fully defined only for |00…0⟩ inputs, but is guaranteed to at least be unitary for other inputs.

My suggestion would thus be to add a check similar to the CanEmulate method in EstimateFrequencyA that only uses the emulation feature if the register starts off in the all-zeros state and is large enough to get some advantage from the emulation feature.

@IrinaYatsenko
Copy link
Contributor Author

IrinaYatsenko commented Nov 10, 2020

My suggestion would thus be to add a check similar to the CanEmulate method in EstimateFrequencyA that only uses the emulation feature if the register starts off in the all-zeros state and is large enough to get some advantage from the emulation feature.

In general we cannot check the register for being in zero state, can we? I can change the API on the native side to return bool and return false without modifying the state, if the prerequisites aren't met. The caller then can fallback to Q# implementation. The disadvantage of this approach is that we'll unpack amplitudes even if we end up not using them. Another way is to add something like bool CanInjectState(qubits) to the public C-API surface of the native simulator and call it before calling InjectState. In some sense, it's cleaner, but I'm hesitant to grow the C-API surface of the simulator too gratuitously... I think, I'll go with the first of the two solutions.

Note: Before I update the runtime not to throw, added a catch so we can run all tests and discover other potential problems. This is not meant to be final.

@cgranade
Copy link
Contributor

In general we cannot check the register for being in zero state, can we?

I think there's already a native API for that, used to implement AssertMeasurement and AssertMeasurementProbability?

    double JointEnsembleProbability(std::vector<Gates::Basis> bs, std::vector<logical_qubit_id> qs)
    {
        removeIdentities(bs, qs);
        if (bs.empty())
        {
            return 0.0;
        }

        recursive_lock_type l(mutex());
        changebasis(bs, qs, true);
        double p = psi.jointprobability(qs);
        changebasis(bs, qs, false);
        return p;
    }

https://github.com/microsoft/qsharp-runtime/blob/1224db00db42333316692f8242129101292ab4e5/src/Simulation/Native/src/simulator/simulator.hpp#L56

I can change the API on the native side to return bool and return false without modifying the state, if the prerequisites aren't met. The caller then can fallback to Q# implementation. The disadvantage of this approach is that we'll unpack amplitudes even if we end up not using them.

That makes a lot of sense, yeah. Hopefully should be able to see performance implications of that unpacking in end-to-end benchmarks.

Another way is to add something like bool CanInjectState(qubits) to the public C-API surface of the native simulator and call it before calling InjectState. In some sense, it's cleaner, but I'm hesitant to grow the C-API surface of the simulator too gratuitously... I think, I'll go with the first of the two solutions.

Makes sense, agreed; if it turns out from benchmarks that we need to grow the C-API surface further, maybe we could generalize slightly to allow checking registers of qubits against |00…0⟩, then do the check from the C# side?

Note: Before I update the runtime not to throw, added a catch so we can run all tests and discover other potential problems. This is not meant to be final.

Sounds good!

@IrinaYatsenko
Copy link
Contributor Author

IrinaYatsenko commented Nov 10, 2020

I think there's already a native API for that, used to implement AssertMeasurement and AssertMeasurementProbability?

I meant to say, that there is no generic Q# way to check the state of the register because only specific simulators can do this, for example the full state simulator. And as long as we have to ping the simulator anyway, we can do the check inside InjectState API or in an additional call as I described above. Also, to guarantee the prereq, AssertMeasurementProbability has to be done on each qubit separately, right? So if we do it on Q# side it will have to make n roundtrips to the simulator rather than a single call if we do the check inside InjectState.

Note: Considered not doing the check at all and allowing to inject the state anyway. Unfortunately, in the current implementation of partial state injection this might not end up being a unitary operation so cannot go that route.

Hopefully should be able to see performance implications of that unpacking in end-to-end benchmarks.

Even if we do see the impact, I'm not sure it's worth optimizing here, because the result of the behaviour is going to be undefined anyway, no?

However, there is one more concern I have now. If I understand your previous explanations correctly, we must provide precise emulation of adjoint as well, otherwise base*adjoint might not yield identity. UPD: discussed with @alexeib2 -- there is no known efficient way to uncompute state injection so the discrepancy is unavoidable. QML scenarios shouldn't need the adjoint, and for those scenarios that do apply it there probably should be a way to configure the libraries (or the simulator) to never emulate.

Copy link
Contributor

@cgranade cgranade 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 looks good, thanks for doing this! Note that we won't see improvements in QML performance until we port that library to use _PrepareAmplitudesFromZeroState.

IrinaYatsenko and others added 2 commits November 17, 2020 14:03
a typo in comment

Co-authored-by: Chris Granade <chgranad@microsoft.com>
@IrinaYatsenko IrinaYatsenko merged commit ae29d5f into main Nov 18, 2020
@IrinaYatsenko IrinaYatsenko deleted the irinayat/nsim-state-prep branch November 18, 2020 01:21
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.

4 participants