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

LRE Inference Functions #2447

Merged
merged 29 commits into from
Aug 27, 2024
Merged

LRE Inference Functions #2447

merged 29 commits into from
Aug 27, 2024

Conversation

purva-thakre
Copy link
Collaborator

@purva-thakre purva-thakre commented Jul 10, 2024

Description

Fixes #2372

To Dos

  • Add details to docstrings
  • Add unit tests with chunking
  • Make mypy happy
  • Unit tests for sample matrix
  • Function and unit test for linear combination coefficients function

License

  • I license this contribution under the terms of the GNU GPL, version 3 and grant Unitary Fund the right to provide additional permissions as described in section 7 of the GNU GPL, version 3.

Before opening the PR, please ensure you have completed the following where appropriate.

Copy link

codecov bot commented Jul 10, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.71%. Comparing base (02ebc4b) to head (f0c0293).
Report is 66 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2447      +/-   ##
==========================================
+ Coverage   98.70%   98.71%   +0.01%     
==========================================
  Files          88       89       +1     
  Lines        4083     4133      +50     
==========================================
+ Hits         4030     4080      +50     
  Misses         53       53              

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


🚨 Try these New Features:

@purva-thakre purva-thakre marked this pull request as ready for review July 18, 2024 03:01
Copy link
Contributor

@vprusso vprusso left a comment

Choose a reason for hiding this comment

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

Overall looks great. Nice work, @purva-thakre !

Added some comments, but many of them are quite minor.

mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/tests/test_multivariate_richardson.py Outdated Show resolved Hide resolved
Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

Very cool stuff here.

I left some comments related to things Vincent raised. I think we can make some big simplifications with how we do monomials, but otherwise looking good.

@purva-thakre purva-thakre mentioned this pull request Jul 21, 2024
6 tasks
@purva-thakre purva-thakre requested a review from natestemen August 1, 2024 09:21
Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

Nice changes! I'm hopeful we can simplify things a bit more, but I might not have the full picture in my head to understand why certain things need to be that way.

mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
@purva-thakre
Copy link
Collaborator Author

purva-thakre commented Aug 16, 2024

@natestemen This is ready for a review with your suggestions.

I removed 2 unit tests in 42fd2b1 because np.sum is not behaving properly. I am looking into this.

When I add the unit test to check if all the coeffs sum up to 1, the tests fail. But if I manually check this value in a jupyter notebook by using np.sum(coeffs), they sum up close to 1. 🤷🏾‍♀️

I figured this was a float addition issue. Rounding the values up to 2 decimal places before calculating the sum is also not working here.

Edit: I think this is a bug in pytest.

Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

Nice clean up! Getting closer!

I'm lacking some understanding as to how the functions added here fit together, but maybe that will come in the final PR? This is usually where I would rely on the PR description to add context for the reviewer.

Comment on lines 119 to 121
assert (
expected_matrix - sample_matrix(test_circ, test_degree, 1)
).all() <= 1e-3
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assert (
expected_matrix - sample_matrix(test_circ, test_degree, 1)
).all() <= 1e-3
assert np.allclose(expected_matrix, sample_matrix(test_circ, test_degree, 1), atol=1e-3)

I don't think the test, as currently written, is testing what is intended. Calling .all on a numpy array returns a boolean indicating whether all the values of the array are truthy. Currently things are passing because expected_matrix is exactly equal to the output of sample_matrix, meaning the array is all 0's. The .all call then returns False and False <= 1e-3 evaluates to True. The right output, but incorrect path to get there.

Other tests need to be reworked to fix this as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for catching this!

Apparently, the two failures I noticed in #2447 (comment) were related to this.

Because the tests passed, I thought there was a float addition error. I will work on finding a way around the for block where I was copying the array.

mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
@pytest.mark.parametrize(
"test_num_layers, test_degree",
[(1, 1), (2, 2), (3, 2), (10, 4)],
# Note need to add (100, 2) here. This makes the unit test very slow.
Copy link
Member

Choose a reason for hiding this comment

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

This should either be removed, or we'll need to refactor _full_monomial_basis_term_exponents to speed it up. If we need to speed it up, what are the largest values it should be performant at?

Copy link
Collaborator Author

@purva-thakre purva-thakre Aug 16, 2024

Choose a reason for hiding this comment

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

we'll need to refactor _full_monomial_basis_term_exponents to speed it up

TO DO: I was thinking of leaving this for the immediate future.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good. Let's finish the full implementation and then come back to it. Please add TODO to the comment so it's a bit more discoverable.

mitiq/lre/tests/test_multivariate_richardson.py Outdated Show resolved Hide resolved
Comment on lines 152 to 166
for i in range(mat_row):
if i == 0: # first row
new_mat = np.concatenate(
(repl_row, input_sample_matrix[1:]), axis=0
)
elif i == mat_row - 1: # last row
new_mat = np.concatenate(
(input_sample_matrix[:i], repl_row), axis=0
)
else:
frst_sl = np.concatenate(
(input_sample_matrix[:i], repl_row), axis=0
)
sec_sl = input_sample_matrix[i + 1 :]
new_mat = np.concatenate((frst_sl, sec_sl), axis=0)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@natestemen @vprusso Does this sound like a better alternative to the previous version where I was copying the sample matrix?

Both of you pointed out memory issues with using .copy() on a numpy array.

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems okay from my perspective!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Discussed with @natestemen to revert back to using .copy() on a numpy array because np.concatenate might have the same memory issues as .copy().

The code is more readable with .copy(). Will add this as a todo as discussed in #2447 (comment)

+ "the matrix is too large. Consider chunking your"
+ " input circuit. "
)
assert det != 0.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to check whether it's close to 0.0 and add an appropriate debug message in case it is not?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Discussed this with @natestemen yesterday. We are coming back to think about edge cases like this once the general LRE implementation is complete.

I'll go ahead and create an issue to log all the todos I need to tackle in the immediate future.


coeff_list = []
mat_row, mat_cols = input_sample_matrix.shape
assert mat_row == mat_cols
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar question here as to whether we want to provide more details to the end-user as to what the issue would have been here if this assert fails.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Returns:
List of the evaluated monomial basis terms using the scale factor
vectors.
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add error handling?

if degree < 1:
    raise ValueError("Degree must be a positive integer")
if fold_multiplier < 1:
    raise ValueError("Fold multiplier must be a positive integer")
if num_chunks is not None and num_chunks < 1:
    raise ValueError("Number of chunks must be a positive integer")

Comment on lines 152 to 166
for i in range(mat_row):
if i == 0: # first row
new_mat = np.concatenate(
(repl_row, input_sample_matrix[1:]), axis=0
)
elif i == mat_row - 1: # last row
new_mat = np.concatenate(
(input_sample_matrix[:i], repl_row), axis=0
)
else:
frst_sl = np.concatenate(
(input_sample_matrix[:i], repl_row), axis=0
)
sec_sl = input_sample_matrix[i + 1 :]
new_mat = np.concatenate((frst_sl, sec_sl), axis=0)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems okay from my perspective!

Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

Approved, but a few small cleanup things to do before merging!

Well done with this Purva!

mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
Comment on lines +34 to +37
We assume the terms in the monomial basis are arranged in a graded
lexicographic order such that the terms with the highest total degree are
considered to be the largest and the remaining terms are arranged in
lexicographic order.
Copy link
Member

Choose a reason for hiding this comment

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

I believe the ordering we are using here is some sort of anti-lexicographic ordering within each "grade". E.g. with grade = 1, in lexicographic ordering 10 comes after 01 since 0 comes before 1. Maybe it's "graded reverse lexicographic ordering"?\

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

raded reverse lexicographic ordering

No, this would be a different order. See section I of https://pi.math.cornell.edu/~dmehrle/notes/old/alggeo/07MonomialOrdersandDivisionAlgorithm.pdf

In the graded lexicographic order, the highest total degree is the largest along with the requirements of the lexicographic order which considers the difference between the exponents.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, so I think all the confusion boils down to a potential confusion in convention.

A tuple of exponents $(a_1, a_2, \ldots, a_n)$ can be taken to represent either $x_1^{a_1}x_2^{a_2}\cdots x_n^{a_n}$ or $x_1^{a_n}x_2^{a_{n-1}}\cdots x_n^{a_1}$. The choice here is unimportant in it's own right, but important that we know which one we are working in and stay consistent.

If we are working in the second convention (which seems a bit strange to me1) then I think everything is correct. In my head, I was previously working with the former convention which means the graded lexicographic ordering on monomials with two terms and maximum degree two ($1 &lt; x_2 &lt; x_1 &lt; x_2^2 &lt; x_1x_2 &lt; x_1^2$) would read

$$(0, 0) < (0, 1) < (1, 0) < (0, 2) < (1, 1) < (2, 0)$$

whereas the latter convention reads

$$(0, 0) < (1, 0) < (0, 1) < (2, 0) < (1, 1) < (0, 2).$$

The latter convention indeed yields what is currently implemented.

So the question is does $(0, 1)$ represent $x_1$ or $x_2$?

Footnotes

  1. It's convention, though, not gospel, so 🤷🏻.

mitiq/lre/inference/multivariate_richardson.py Outdated Show resolved Hide resolved
Comment on lines +117 to +119
# verify the matrix is square
mat_row, mat_cols = sample_matrix.shape
assert mat_row == mat_cols
Copy link
Member

Choose a reason for hiding this comment

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

We typically use tests for this type of thing. Is there a reason we should have this assert in the code here?

Copy link
Collaborator Author

@purva-thakre purva-thakre Aug 27, 2024

Choose a reason for hiding this comment

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

I am making my way through Dan Bader's 'Python Tricks: The Book' to help me write better code. He recommends using assertions like this.

@purva-thakre purva-thakre merged commit d22df7b into main Aug 27, 2024
18 checks passed
@purva-thakre purva-thakre deleted the lre_inference branch August 27, 2024 15:56
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.

Multivariate inference for LRE
3 participants