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

Projectors for TensorFlow quantum #4331

Merged
merged 31 commits into from Jul 28, 2021

Conversation

tonybruguier
Copy link
Contributor

TensorFlow Quantum would like to be able to specify projectors in a memory efficient way. This PR attempts to provide such a facility.

@google-cla google-cla bot added the cla: yes Makes googlebot stop complaining. label Jul 18, 2021
@tonybruguier tonybruguier marked this pull request as ready for review July 18, 2021 06:54
@tonybruguier tonybruguier requested review from cduck, vtomole and a team as code owners July 18, 2021 06:54
@tonybruguier
Copy link
Contributor Author

I would suggest @MichaelBroughton as a first-pass reviewer but happy to get advice from anyone.

@tonybruguier tonybruguier mentioned this pull request Jul 18, 2021
Copy link
Collaborator

@MichaelBroughton MichaelBroughton left a comment

Choose a reason for hiding this comment

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

This implementation is too general. We want to support just computational bitstrings with these projectors. With the code as it is now I can do this:

q0 = cirq.GridQubit(0, 0)
phi = [<numbers>, <numbers>]
complicated_projector = cirq.ProjectorString({q0: cirq.Projector(phi)})

psi = cirq.Circuit(<lots of gates>)

Using TFQ if I wanted to serialize phi (with tfq.convert_to_tensor) it would require exponential memory on the wire to write down the state to project on to. It is very important for performance reasons in TFQ that wire size be kept down.

In addition to this, if I wanted to use a quantum computer to evaluate something like:

image

For completely arbitrary phi and psi I would need to use a swap test which isn't very NiSQY and would introduce complications in trying to preserve the differentiability of TFQ expectation calculations (let alone also trying to write down a circuit that actually prepares an arbitrary state if all you are given is just the state phi).

If the scope of functionality were to be reduced to:

q0 = cirq.GridQubit(0, 0)
q1 = cirq.GridQubit(0, 1)
q2 = cirq.GridQubit(0, 2)

# !only support computational basis states!
simple_phi = cirq.ProjectorString({q0: 0, q1: 1, q2: 0})
psi = cirq.Circuit(<lots of gates>)

Now, if I wanted to serialize simple_phi using tfq.convert_to_tensor all I would need to put on the wire is a mapping from qubits to ints indicating which bitstring projector I have instead of having to write down the full state vector(s) on the qubits.

If I want to evaluate:

image

It's very cheap to do because it's just:

image

which boils down to running the circuit you have to prepare psi, drawing some samples and then counting the number of times you see the 010 bitstring. No swap test or trying to figure out how to prepare an arbitrary phi. This computational bitstring implementation of Projectors will also make it easier to incorporate differentiability of the Projector type into the tfq.***_expectation C++ ops without taking any major performance hits.

All that being said I think all we really need here is a way to do things like this:

q0 = cirq.GridQubit(0, 0)
q1 = cirq.GridQubit(0, 1)
q2 = cirq.GridQubit(0, 2)

a = cirq.ProjectorString({q0: 0, q1: 1, q2: 0})
b = cirq.ProjectorString({q1: 1, q2: 1})

c = a + b  # type(c) == cirq.ProjectorSum

c.expectation_from_***(<whatever>)
a.expectation_from_***(<whatever>)
c.matrix()  # type(c.matrix()) == scipy.sparsematrix

# etc.

Which is a lot simpler than what we have now.

@tonybruguier
Copy link
Contributor Author

@MichaelBroughton
Thanks for the quick turn-around on the review and its detailed explanation. I updated the code and I think it follows the rough idea. A few remarks:

  • The ProjectorSum object looks a lot like PauliSum but since you had suggested a separate object, IIUC. I'm happy to try to use PauliSum instead or leave as is (though I am not sure using PauliSum would be easy). Alternatively, we could ask for advice at the next group meeting.

  • I could split the PR in smaller bits, if you want. For example, a first one could be only for ProjectorString and a second one for ProjectorSum.

  • I realize that your comment suggested to use sparse matrices, but for simplicity, I only use dense ones. I do plan to address this (and left a TODO in my name). Note that the serialization of the object is already small in memory, as there's only one integer; It's only the simulation that is a little sub-optimal.

Note I don't feel very strongly about the decisions I've made. It's mostly decisions I had to make to make progress. Thanks again for the review. PTAL.

@MichaelBroughton
Copy link
Collaborator

The ProjectorSum object looks a lot like PauliSum but since you had suggested a separate object, IIUC. I'm happy to try to use PauliSum instead or leave as is (though I am not sure using PauliSum would be easy). Alternatively, we could ask for advice at the next group meeting.

The point is that it look and feel very similar, but give the clear indication that you are dealing with Projectors and not Paulis. We could also surface this for more discussion, but I think this is a pretty uncontroversial approach.

I could split the PR in smaller bits, if you want. For example, a first one could be only for ProjectorString and a second one for ProjectorSum.

That sounds like a good idea. Let's start with just ProjectorStrings.

I realize that your comment suggested to use sparse matrices, but for simplicity, I only use dense ones. I do plan to address this (and left a TODO in my name). Note that the serialization of the object is already small in memory, as there's only one integer; It's only the simulation that is a little sub-optimal.

This certainly is an important piece . If we go one PR at a time, it shouldn't be that much more work to make sure that when we merge things, the performance is closer to optimal. As an example the expectation_from_*** functions can certainly be trimmed down a lot. Probably to something like:

def expectation_from_state_vector(
        self,
        state_vector: np.ndarray,
        qid_map: Mapping[raw_types.Qid, int],
        *,
        atol: float = 1e-7,
        check_preconditions: bool = True,
    ) -> float:
return np.abs(state_vector[<projector_bitstring>]) ** 2

would work

@MichaelBroughton MichaelBroughton self-assigned this Jul 21, 2021
@tonybruguier
Copy link
Contributor Author

Thanks, @MichaelBroughton , PTAL when you have some time?

Copy link
Collaborator

@MichaelBroughton MichaelBroughton left a comment

Choose a reason for hiding this comment

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

Mostly nits now. After this round of review I think we will be good to merge. The only remaining major item is tightening up the matrix method (similar to what was done for the expectation_*** methods).

cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/projector_test.py Show resolved Hide resolved
cirq-core/cirq/ops/projector_test.py Show resolved Hide resolved
cirq-core/cirq/ops/projector_test.py Show resolved Hide resolved
cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
@tonybruguier
Copy link
Contributor Author

Thanks, @MichaelBroughton PTAL when you can.

Copy link
Collaborator

@MichaelBroughton MichaelBroughton left a comment

Choose a reason for hiding this comment

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

last nit then LGTM.

cirq-core/cirq/ops/projector.py Outdated Show resolved Hide resolved
@tonybruguier
Copy link
Contributor Author

Thanks @MichaelBroughton! I copied over your suggestion, merged from the main branch, and re-ran the tests.

Copy link
Collaborator

@MichaelBroughton MichaelBroughton left a comment

Choose a reason for hiding this comment

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

LGTM

@MichaelBroughton MichaelBroughton added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Jul 28, 2021
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Jul 28, 2021
@CirqBot CirqBot merged commit 9a38c2d into quantumlib:master Jul 28, 2021
@CirqBot CirqBot removed automerge Tells CirqBot to sync and merge this PR. (If it's running.) front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. labels Jul 28, 2021
@tonybruguier tonybruguier deleted the projector_strings branch July 31, 2021 03:26
CirqBot pushed a commit that referenced this pull request Aug 19, 2021
This is a follow-up on #4331 which introduced ProjectorString objects. The present PR aims at adding ProjectorSum objects.
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
TensorFlow Quantum would like to be able to specify projectors in a memory efficient way. This PR attempts to provide such a facility.
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
This is a follow-up on quantumlib#4331 which introduced ProjectorString objects. The present PR aims at adding ProjectorSum objects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Makes googlebot stop complaining.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants