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

Speed up GroverOperator and add GlobalPhase to its decomposition #4666

Merged
merged 25 commits into from
Nov 23, 2023

Conversation

dwierichs
Copy link
Contributor

@dwierichs dwierichs commented Oct 12, 2023

Context:

  1. The GroverOperator is applied by DefaultQubit by applying its matrix for up to 12 wires (incl).
    However, GroverOperator has a lot of structure as it is 2*P-1 for a projector P, and a custom application rule is faster than constructing and applying the matrix.
  2. The compute_matrix method of GroverOperator in addition uses a rather contrived way to construct the matrix np.full((dim, dim), normalization_constant). Instead, we can simply use the broadcasting behaviour of numpy and do normalization_constant - np.eye(dim) (timings below).
  3. The GroverOperator decomposition is only correct up to a minus sign (or phase of $\pi$).

Description of the Change:

  1. Adds a dispatch registration for apply_operation in /qubit/apply_operation.py for GroverOperator that is not based on the matrix representation of GroverOperator.
  2. Modifies the compute_matrix method of GroverOperator to skip repetitive calling of np.kron and np.outer to construct a trivial matrix.
  3. Introduces a GlobalPhase($\pi$) gate in the decomposition.

Benefits:
Speedups and correct decomposition.

Possible Drawbacks:
N/A

Related GitHub Issues:

Timing code for the matrix construction:

def f(n_wires):
    s1 = np.array([1, 1]) / np.sqrt(2)
    # uniform superposition state |s>
    s = reduce(np.kron, list(it.repeat(s1, n_wires)))
    # Grover diffusion operator
    return 2 * np.outer(s, s) - np.identity(2**n_wires)

for N in [3, 6, 9, 12]:
    dim = 2**N

    print(f"N={N}")
    %timeit f(N)
    %timeit 2 / dim - np.eye(dim)
    print()

image

Timings for the different apply_operation options, for three scenarios: Number of wires for operation and state is equal, number of wires for state is that for operation +4, and number of wires for state is double of that for operation.

Given the results, we could do einsum for len(op.wires)<8 and the dispatch method for bigger wire counts.

Screenshot from 2023-10-12 14-03-56
image
image

@codecov
Copy link

codecov bot commented Oct 12, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (17503d3) 99.65% compared to head (cd34615) 99.64%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4666      +/-   ##
==========================================
- Coverage   99.65%   99.64%   -0.01%     
==========================================
  Files         383      383              
  Lines       34559    34311     -248     
==========================================
- Hits        34439    34190     -249     
- Misses        120      121       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@albi3ro albi3ro left a comment

Choose a reason for hiding this comment

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

You may need to tweak "use matrix versus decomposition" threshold in default qubit as well.

It is currently located here:

if op.name == "GroverOperator" and len(op.wires) >= 13:

But I have an open PR that will be moving it. We can resolve that merge conflict when we get to it.

@dwierichs
Copy link
Contributor Author

You may need to tweak "use matrix versus decomposition" threshold in default qubit as well.

Good call. I actually removed it now, because it is supposed to address the large wire counts, which is the regime where the dispatched apply_grover is particularly fast. Moreover, as the decomposition also creates a dense matrix (via MultiControlledX), this PR makes GroverOperator accessible for qubit counts (>=15 on my laptop) that were inaccessible before :)

But I have an open PR that will be moving it. We can resolve that merge conflict when we get to it.

👍

@dwierichs
Copy link
Contributor Author

dwierichs commented Nov 13, 2023

Updated timings for all interfaces (line style gives interface, color gives target wires): As there is an outlier in Tensorflow which leads to num_wires=16, num_op_wires=8 having a factor 2 slowdown, still, I will move the threshold for falling back to matrix-based method (einsum/tensordot) from num_op_wires<8 to num_op_wires<9.

image

Copy link
Contributor

@timmysilv timmysilv left a comment

Choose a reason for hiding this comment

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

pretty much good to go, just a few questions

doc/releases/changelog-dev.md Show resolved Hide resolved
pennylane/devices/qubit/apply_operation.py Outdated Show resolved Hide resolved
tests/devices/qubit/test_apply_operation.py Show resolved Hide resolved
tests/devices/qubit/test_apply_operation.py Show resolved Hide resolved
tests/templates/test_subroutines/test_grover.py Outdated Show resolved Hide resolved
Copy link
Contributor

@timmysilv timmysilv left a comment

Choose a reason for hiding this comment

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

great optimization, thanks for putting this all together! 🎉

timmysilv added a commit that referenced this pull request Nov 21, 2023
**Context:**
GlobalPhase is a relatively new operator, and some plugins may not be
able to handle it. To be consistent with the pre-GlobalPhase days, I'm
making it decompose to nothing so devices will decompose it and make it
disappear.

**Description of the Change:**
Implement `GlobalPhase.compute_decomposition` so it decomposes to
nothing

**Benefits:**
Plugins won't break if they see it. It does have a matrix defined, but
maybe some plugins need a whitelist of operators.

**Possible Drawbacks:**
Maybe plugins should fail if they see it, and maybe that's what we
wanted?

**Related GitHub Issues:**
Good to merge with/before #4666 
[sc-50428]

---------

Co-authored-by: Tom Bromley <[email protected]>
Co-authored-by: Christina Lee <[email protected]>
@dwierichs dwierichs merged commit 1e6c11d into master Nov 23, 2023
34 checks passed
@dwierichs dwierichs deleted the grover-mat branch November 23, 2023 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants