-
Notifications
You must be signed in to change notification settings - Fork 616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds a convenience prob()
measurement function to return the probabilities of all computational basis states in a single QNode evaluation
#432
Conversation
prob()
measurement function to return the probabilities of all computational basis states in a single QNode evaluationprob()
measurement function to return the probabilities of all computational basis states in a single QNode evaluation
Codecov Report
@@ Coverage Diff @@
## master #432 +/- ##
=========================================
Coverage ? 99.29%
=========================================
Files ? 45
Lines ? 3426
Branches ? 0
=========================================
Hits ? 3402
Misses ? 24
Partials ? 0
Continue to review full report at Codecov.
|
prob()
measurement function to return the probabilities of all computational basis states in a single QNode evaluationprob()
measurement function to return the probabilities of all computational basis states in a single QNode evaluation
|
||
return OrderedDict(zip(states, probs)) | ||
wires = wires or range(self.num_wires) | ||
wires = np.hstack(wires) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come this line would be needed? One of the reasons could be flattening wires
here, however, would it not be required to have wires
already as a flat list
or an array
? (Also this performs the implicit conversion on range(self.num_wires)
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I can't remember why I needed to add this line 🤔
|
||
This measurement function accepts no observables, and instead | ||
instructs the QNode to return a flat array containing the | ||
probabilities of each quantum state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an extra sentence, it might be worth adding something like the explanation in the example such as
The dimension of the computational basis states is equal to the number of wires specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to say
Marginal probabilities may also be requested by restricting
the wires to a subset of the full system; the size of the
returned array will be ``[2**len(wires)]``.
wires (Sequence[int] or int): the wire the operation acts on | ||
""" | ||
# pylint: disable=protected-access | ||
op = qml.Identity(wires=wires, do_queue=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently qml.Identity
inherits from CVObservable
, how come this works in the qubit case? Would have expected that there is a check somewhere that would throw an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks the qnodes/base.py
explicitly makes an exception for Identity
:
# True if op is a CV, False if it is a discrete variable (Identity could be either)
are_cvs = [
isinstance(op, CV) for op in self.queue + list(res) if not isinstance(op, qml.Identity)
]
@pytest.fixture | ||
def init_state(scope="session"): | ||
"""Fixture that creates an initial state""" | ||
def _init_state(n): | ||
"""An initial state over n wires""" | ||
state = np.random.random([2 ** n]) + np.random.random([2 ** n]) * 1j | ||
state /= np.linalg.norm(state) | ||
return state | ||
|
||
return _init_state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be nice to have this in the conftest.py
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, that's a good point. I'm a bit hesitant to put it in the conftest.py
file right away, as the conftest file is a bit... messy, and needs cleaning up (there are two many fixtures which aren't used or aren't needed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really nice @josh146 ! Had a couple of minor comments, most we have already discussed
Context:
Currently, due to the 'expectation of observable' abstraction we use, the only way to return the probabilities from a QNode is to use a single QNode per computational basis state, and use
return qml.expval(qml.Hermitian(|x><x|, wires=All))
. This can be computationally expensive, especially if the size of each projector is huge.However, probability is already computed via a single QNode evaluation (analytically on simulators and estimated on hardware), so there is no reason not to provide a way to return it from QNodes. Furthermore, since it is simply an array of expectation values of projectors, it supports analytic differentiation. That said, it does necessitate a slightly different UI, since it doesn't correspond to a single expectation of a single observable (as we currently require).
Example:
Output:
Description of the Change:
Adds the
qml.probs()
measurement function (from a UI perspective, roughly equivalent to theexpval
,sample
,var
measurement functions).Unlike the other measurement functions, it does not accept an observable. Instead,
probs()
accepts a required argumentwires
, determining the marginal probabilities to return.prob(wires=range(num_wires))
will return the full joint probability distribution.Unlike the other measurement functions, which return a scalar,
probs(wires=range(num_wires))
will return an array of size(2**num_wires,)
Adds a new observable return type enum to
qml.operation
,Probability
.If an observable with
obs.return_type=Probability
is present, the QNode has a new logic flow:self.output_conversion = np.squeeze
(to avoid superfluous nesting)self.output_dim = 2**len(obs.wires)
Device.execute()
returns the result ofself.probability(wires=wires)
.The ability to return marginal probabilities has been added to
DefaultQubit.probability()
.Benefits:
Now possible to return and differentiate the probabilities of a quantum state, with very little modification.
UI should apply equally well to the CV case, with the addition of a
cutoff
argument.Possible Drawbacks:
Related GitHub Issues: n/a