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

[Testing] Fix types in operations for dense pauli strings #4892

Closed
wants to merge 20 commits into from
Closed

[Testing] Fix types in operations for dense pauli strings #4892

wants to merge 20 commits into from

Conversation

alexbouayad
Copy link

@alexbouayad alexbouayad commented Jan 25, 2022

Fix #4835.

Fix operations like __pow__, __mul__, __rmul__, __abs__, tensor_product, etc, on abstract class BaseDensePauliString so that the operations return compatible types when applied on the two concrete subclasses DensePauliString and MutableDensePauliString.

Also add a few type annotations, and modify a few tests.

One noticeable implied change: multiplying a MutableDensePauliString object with a DensePauliString object (and vise versa) is not allowed anymore.

@alexbouayad alexbouayad changed the title [Testing] Fix operation types for dense pauli strings Fix operation types for dense pauli strings Jan 25, 2022
@alexbouayad alexbouayad changed the title Fix operation types for dense pauli strings [Testing] Fix operation types for dense pauli strings Jan 25, 2022
@alexbouayad alexbouayad changed the title [Testing] Fix operation types for dense pauli strings Fix operation types for dense pauli strings Jan 25, 2022
@alexbouayad alexbouayad changed the title Fix operation types for dense pauli strings Fix types in operations for dense pauli strings Jan 25, 2022
@CirqBot CirqBot added the size: M 50< lines changed <250 label Jan 25, 2022
@alexbouayad alexbouayad changed the title Fix types in operations for dense pauli strings [Testing] Fix types in operations for dense pauli strings Jan 25, 2022
@alexbouayad alexbouayad changed the title [Testing] Fix types in operations for dense pauli strings Fix types in operations for dense pauli strings Jan 25, 2022
cirq-core/cirq/ops/dense_pauli_string.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/dense_pauli_string.py Outdated Show resolved Hide resolved
pauli_mask=mask,
coefficient=self.coefficient * _vectorized_pauli_mul_phase(self.pauli_mask[i], p),
)

return NotImplemented

def __rmul__(self, other):
if isinstance(other, (sympy.Basic, numbers.Number)):
def __rmul__(self: T, other: Any) -> T: # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the type ignore? (here and elsewhere). Should we revert to not having type annotations in these cases instead of explicitly ignoring them?

Copy link
Author

Choose a reason for hiding this comment

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

The thing here is that BaseDensePauliString is a subclass of raw_types.Gate. As a result, mypy expects BaseDensePauliString.__rmul__ to return an object of a subclass of cirq.LinearCombinationOfGates. That is not the case, as the operation actually acts on the coefficient attribute of the Pauli string. Maybe a natural fix would be for the operations in raw_types.Gate to have Union[Gate, cirq.LinearCombinationOfGates] as return type instead of cirq.LinearCombinationOfGates alone. I'll commit the fix for review.

Copy link
Author

Choose a reason for hiding this comment

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

I rollbacked the previous change, that is, the return type of linear operations in raw_types.py is now back to cirq.LinearCombinationOfGates alone. I used Gate's method wrap_in_linear_combination to convert the results of linear operations in dense_pauli_string.py (I mimicked what's done in raw_types.py).

cirq-core/cirq/ops/dense_pauli_string.py Outdated Show resolved Hide resolved
@tanujkhattar tanujkhattar self-assigned this Feb 1, 2022
Copy link
Collaborator

@tanujkhattar tanujkhattar left a comment

Choose a reason for hiding this comment

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

Left another round of comments. Main concern is about potential breaking changes when doing binary operations on dense pauli strings of different type types.

if isinstance(power, int):
i_group = [1, +1j, -1, -1j]
if self.coefficient in i_group:
coef = i_group[i_group.index(self.coefficient) * power % 4]
else:
coef = self.coefficient ** power
if power % 2 == 0:
return coef * DensePauliString.eye(len(self))
return DensePauliString(coefficient=coef, pauli_mask=self.pauli_mask)
return coef * self.eye(len(self))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be type(self).eye since eye is a class method. We should also have a test to verify this works as expected.

Suggested change
return coef * self.eye(len(self))
return coef * type(self).eye(len(self))

Copy link
Author

@alexbouayad alexbouayad Mar 15, 2022

Choose a reason for hiding this comment

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

  • self.eye and type(self).eye are semantically equivalent here. Is your suggestion referring to some convention or best practice?
  • I'll add the test.

Copy link
Author

Choose a reason for hiding this comment

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

I changed to type(self) as suggested.

def __mul__(self: TCls, other: Union[sympy.Basic, int, float, complex, TCls]) -> TCls:
cls = type(self)

if isinstance(other, cls):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this mean multiplying a MutableDensePauliString instance with a DensePauliString was allowed before this change but will not be allowed anymore? This sounds like a breaking change and should not be done.

For binary operations that expect an other argument which can be a base dense pauli string, I think we should default to returning a MutableDensePauliString if the types of self and other are not same (but both are instances of BaseDensePauliString), else return type(self).

This should be done for __mul__, __rmul__, tensor_product etc. Please also add corresponding tests.

Copy link
Author

Choose a reason for hiding this comment

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

I added the possibility to cross operate on mutable and non-mutable dense Pauli strings.

cirq-core/cirq/ops/dense_pauli_string.py Outdated Show resolved Hide resolved
cirq-core/cirq/ops/raw_types.py Outdated Show resolved Hide resolved
@vtomole
Copy link
Collaborator

vtomole commented May 8, 2022

Friendly ping @alexandrebouayad: Do you have time to finish this up? I can take it on if not.

@alexbouayad
Copy link
Author

Friendly ping @alexandrebouayad: Do you have time to finish this up? I can take it on if not.

@vtomole thanks for the reminder, it had slipped my mind. I'll do it in the coming days.

@alexbouayad
Copy link
Author

I have made the changes as suggested, plus a few other minor ones. I'll add the remaining tests, and I'll ensure that local checks pass by the end of this week hopefully.

@tanujkhattar
Copy link
Collaborator

@alexandrebouayad Can you please fix the remaining failing tests? Thanks!

@tanujkhattar
Copy link
Collaborator

@alexandrebouayad Gentle reminder, do you still have time to work on this?

@alexbouayad alexbouayad changed the title Fix types in operations for dense pauli strings [Testing] Fix types in operations for dense pauli strings Jun 27, 2022
@vtomole vtomole added the triage/duplicate This issue or pull request is addressed by another issue or pull request label Jun 27, 2022
@vtomole
Copy link
Collaborator

vtomole commented Jun 27, 2022

Hey @alexandrebouayad , I'm pretty sure this PR has been superseded by #5624. We would love your review on that PR.

@tanujkhattar
Copy link
Collaborator

@vtomole Actually, I did skip over some of the tricky parts of fixing mypy types in my PR #5624; for example:

Adding return types annotations to operations like __neg__, __add__ etc. breaks mypy because the cirq.Gate class returns LinearCombinationOfGates by default for these methods, but in BaseDensePauliString, we want to return type(self)(...), which is a (subclass of) BaseDensePauliString and not a LinearCombinationOfGates. This breaks Liskov Substitution Principle because BaseDensePauliString is not a subclass of LinearCombinationOfGates.

Fixing this would require us to modify the default return types on cirq.Gate, which will be a big breaking change. We can potentially open a new issue to track this and maybe @alexandrebouayad's PR can tackle the new issue if he's already thought on this (I see the solution he has applied in his PR is to completely remove the return types from cirq.Gate's methods -- I'm not sure if this is the best we can do though; it's probably better to not have return types on a specific implementation like BaseDensePauliString instead of removing return types from cirq.Gate)

@alexbouayad
Copy link
Author

alexbouayad commented Jun 28, 2022

@vtomole Thanks for letting me now, I'll try and have a look at #5624.

@tanujkhattar I have managed to add full return type annotations for both cirq.Gate and BaseDensePauliString (plus DensePauliString and MutableDensePauliString). Mypy seems happy with it.

For the latter to work, I had to remove NotImplementedType from the return types of the binary special methods, as the workaround used in Cirq equates NotImplementedType with Any (leading to theoretical and practical typing inconsistencies). This seems to me to be the natural (or at least a more natural) thing to do, as NotImplemented is actually never returned by (public) operators based on corresponding (private) binary special methods; see https://docs.python.org/3/library/constants.html#NotImplemented.

@alexbouayad alexbouayad closed this by deleting the head repository Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: M 50< lines changed <250 triage/duplicate This issue or pull request is addressed by another issue or pull request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Operations on MutableDensePauliString return a DensePauliString
4 participants