Skip to content
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

Port PennyLane-Cirq to QubitDevice #19

Merged
merged 45 commits into from
Feb 4, 2020
Merged

Port PennyLane-Cirq to QubitDevice #19

merged 45 commits into from
Feb 4, 2020

Conversation

johannesjmeyer
Copy link
Contributor

@johannesjmeyer johannesjmeyer commented Feb 3, 2020

PennyLane introduces the QubitDevice baseclass for plugins in the next release (0.8.0). As QubitDevice eases the implementation drastically, this PR ports the PennyLane-Cirq plugin to this base device class.

As a side effect, PennyLane-Cirq now supports tensor observables.

This PR also adresses some changes in Cirq that resulted in depreceation warnings.

@johannesjmeyer johannesjmeyer added the WIP Work in Progress label Feb 3, 2020
@codecov
Copy link

codecov bot commented Feb 4, 2020

Codecov Report

Merging #19 into master will decrease coverage by 0.05%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master      #19      +/-   ##
==========================================
- Coverage   98.73%   98.67%   -0.06%     
==========================================
  Files           6        6              
  Lines         158      151       -7     
==========================================
- Hits          156      149       -7     
  Misses          2        2
Impacted Files Coverage Δ
pennylane_cirq/cirq_device.py 100% <100%> (ø) ⬆️
pennylane_cirq/simulator_device.py 98.38% <100%> (-0.08%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f3e8865...3c5fe9f. Read the comment docs.

@johannesjmeyer johannesjmeyer changed the title [WIP] Port PennyLane-Cirq to QubitDevice Port PennyLane-Cirq to QubitDevice Feb 4, 2020
# be calculated later
w, U = np.linalg.eigh(Hmat)
self._eigs[Hkey] = {"eigval": w, "eigvec": U}
# TODO: get pre rotated state here
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Contributor Author

@johannesjmeyer johannesjmeyer Feb 4, 2020

Choose a reason for hiding this comment

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

Actually I'm a little confused with the handling of pre-rotation state and rotated state. In the implementation of default.qubit, the probability is given for the rotated state but the state is given for the pre_rotated_state. I think this is somewhat inconsistent, isn't it?

Here in Cirq I have the additional problem that I can't really extract the pre_rotated_state without much hassle so that currently, state is the rotated state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is your opinion on this, @antalszava ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And @josh146

Copy link
Member

Choose a reason for hiding this comment

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

This is slightly intentional. Now that there is qml.prob, we no longer want the user to access dev.probability, so for consistency this should always represent the actual device probability.

For state, however, we are still in an awkward position where there is no equivalent to extract it from the qnode yet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So what do you think of me keeping the post-rotation state as dev.state? It is possible to extract the pre-rotation state, but it will not make the code clearer...

Copy link
Member

Choose a reason for hiding this comment

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

I think it is fine for now. dev.state should only ever be used for debugging, and we can attach caveats to it that depend on the device (e.g. a lot of hardware simulators don't even store any state)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a docstring that explains this

def pre_measure(self):
super().pre_measure()
def apply(self, operations, rotations=None, **kwargs):
super().apply(operations, rotations, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
super().apply(operations, rotations, **kwargs)
super().apply(operations, rotations=rotations, **kwargs)

Copy link
Contributor

Choose a reason for hiding this comment

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

This would be in case kwargs=={} is True and apply being called only with passing operations to it, since then I think rotations would default to None and there might be an error arising from passing too many positional arguments (None is going to be passed as the second argument). Might be wrong though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the argument rotations entirely now to be consistent with the signature of apply from the QubitDevice class.

)
else:
return self.sample(observable, wires, par).var()
return super().generate_samples()
Copy link
Contributor

Choose a reason for hiding this comment

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

How about the case of analytic==True and returning qml.sample?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see no problem with that? The logic of deciding when to call generate_samples is implemented in QubitDevice, so I just have to assure that samples are properly generated when it is called. And for analytic mode I can safely piggyback onto the default implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is the part of the device.execute():

        if (not self.analytic) or circuit.is_sampled:
            self._samples = self.generate_samples()

for self.analytic==True and circuit.is_sampled SimulatorDevice.generate_samples() will be called and since self.analytic==True the if statement evaluates to True on line 158, and QubitDevice.generate_samples() will be called.

Whereas, if self.analytic==False and circuit.is_sampled, SimulatorDevice.generate_samples() still gets called, but the if statement on line 158 evaluates to False and the code block below gets executed.

If this is so (and I didn't confuse myself or mess up something here 😅 ), then how come that there is a different logic for these two cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All you say is totally correct. There is a different logic for the analytic==False case because in that case I don't generate the samples myself but I use Cirq's own measurement scheme. I did this because I wanted to use as much of Cirq's inbuilt API as possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the difference between simulator.run (analytic==False) and simulator.simulate (analytic==True)

("RX", [2], [cirq.Rx(2)]),
("RX.inv", [1.4], [cirq.Rx(-1.4)]),
("RX.inv", [-1.2], [cirq.Rx(1.2)]),
("RX.inv", [2], [cirq.Rx(-2)]),
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps keeping 1-1 rotations tests (e.g. RX, Rot and their inverses) could be nice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are still there, it is just misaligned due to the blacking

Copy link
Contributor

@antalszava antalszava left a comment

Choose a reason for hiding this comment

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

Looks good @johannesjmeyer 😊 Once the comments are addressed, should be merge-ready

@antalszava antalszava merged commit 75c2e52 into master Feb 4, 2020
@@ -1,3 +1,3 @@
pennylane>=0.7
pennylane>=0.8
Copy link
Contributor

Choose a reason for hiding this comment

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

This was extending the functionality of #21 with QubitDevice being used in this plugin.

@co9olguy co9olguy deleted the qubit_device_port branch May 12, 2020 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants