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

Parameterize TypeRow by element type #256

Merged
merged 55 commits into from
Jul 25, 2023

Conversation

acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Jul 10, 2023

There is a mass of churn here but in the end few major changes. There are two main benefits:

  • It makes assumptions clear, for example that (in this version, but largely inherited from main) predicates are classical-only.
  • The possibility of building a ClassicType::Container of a tuple including a Qubit is now statically ruled out
  • It avoids having to declare code to handle some cases that can never occur. For example, ConstTypeError::LinearTypeDisallowed can now be removed, and Signature::get for "other" ports no longer needs an error case

Note this does not follow #253; we could do #253 before or after this, and doing this first will involve significantly more churn, but also be more explicit and probably find more bugs...so I'm doing this first ;)

We retain that there are "kind of" two ways to declare the SimpleType tuple-of-two-ints:

  • SimpleType::Classic of ClassicType::Container of Container::Tuple of TypeRow of two ClassicType::i64()s
  • SimpleType::Qontainer of Container::Tuple of TypeRow of two SimpleType::Classic(ClassicType::i64())s

...the idea being that factory methods like new_predicate, new_tuple and new_sum will pick the correct one.

Drawbacks:

  • I had to change the serialization code (src/types/simple/serialize.rs) so that, rather than SerSimpleType containing TypeRows containing SimpleTypes, SerSimpleType now contains only (TypeRows of) SerSimpleTypes; flattening is thus recursively and once only rather than iteratively one stage at a time.
  • I've disabled some pyo3 for the time being. We might bring it back via wrapper SimpleRow and ClassicRow, say.

Secondly there is a new macro - type_row! builds TypeRow<SimpleType> only, there is now also classic_row!. However, both of these are used (almost) only in tests, so I have to wonder whether we need such control over efficient static allocation for test code. There is a possibility of renaming type_row! to simple_row! (another mass of churn!) but I'm holding off on that for now because we might rename SimpleType to Type or AnyType in which case type_row may be a reasonable name to keep. Renaming can follow in a later PR.

@acl-cqc acl-cqc changed the base branch from main to refactor/linear_classic July 10, 2023 13:53
/// given the rows of each tuple
pub fn predicate_variants_row(variant_rows: impl IntoIterator<Item = TypeRow>) -> Self {
variant_rows
fn try_convert_elems<D: PrimType + TryFrom<T>>(self) -> Result<TypeRow<D>, D::Error> {
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'd like to just have

impl <F: PrimType, T: PrimType+TryFrom<F>> TryFrom<TypeRow<F>> for TypeRow<T>

but get a conflicting instance declaration...

}

/// Converts the elements of this TypeRow into some other type that they can `.into()`
pub fn map_into<T2: PrimType + From<T>>(self) -> TypeRow<T2> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similarly, it'd be good for this just to be an Into/From impl, but again that seems to raise conflicting-instance problems

Base automatically changed from refactor/linear_classic to main July 17, 2023 11:21
Copy link
Collaborator

@aborgna-q aborgna-q left a comment

Choose a reason for hiding this comment

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

Quite a bit of renaming noise, but LGTM

src/types.rs Outdated
Comment on lines 14 to 23
pub use simple::{ClassicRow, ClassicType, Container, SimpleRow, SimpleType};

use smol_str::SmolStr;

use crate::hugr::{Direction, Port};
use crate::utils::display_list;
use crate::{resource::ResourceSet, type_row};

use self::simple::{PrimType, TypeRow};

Copy link
Collaborator

Choose a reason for hiding this comment

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

Export PrimType and TypeRow too?

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 this is something I debate. Obviously Rust requires that these types have to be public, but I'd kinda like to encourage users to use the aliases. The case in which a user should be using TypeRow....almost certainly is in a function parameterized <T: PrimType> ...TypeRow<T>....

Which is not the common case, but it is a reasonable case ;-). So ok, exported (within types but no further, as nothing is exported out of types)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

However @aborgna-q one thing we might do is make PrimType sealed (i.e. can only be implemented inside the crate)....?

src/types/simple.rs Outdated Show resolved Hide resolved
/// let repeated_row: TypeRow = type_row![B; 2];
/// assert_eq!(repeated_row, static_row);
/// let repeated_row: SimpleRow = type_row![B; 3];
/// assert_eq!(repeated_row, sig.output);
/// ```
#[allow(unused_macros)]
#[macro_export]
macro_rules! type_row {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with the renaming... in a separate PR

@aborgna-q
Copy link
Collaborator

I have to wonder whether we need such control over efficient static allocation for test code

It's mostly useful when dealing with Signatures of leaf ops. OpType::signature on something like a CNOT does not need to allocate at all, so checking things on flat dataflow regions needs a lot less indirection.

It may be useless complexity, but we don't have any benchmarks at the moment so I'd wait for that before making any refactoring.

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

I have to wonder whether we need such control over efficient static allocation for test code

It's mostly useful when dealing with Signatures of leaf ops. OpType::signature on something like a CNOT does not need to allocate at all, so checking things on flat dataflow regions needs a lot less indirection.

It may be useless complexity, but we don't have any benchmarks at the moment so I'd wait for that before making any refactoring.

Just to clarify - I did not mean to suggest removal of type_row!. [You give a good example of which I was not aware, to boot!] However, I wonder whether there is really value in having classic_row! as well. Uses thereof outside tests amount to two, in simple.rs, both times empty i.e. classic_row![]. Maybe we can revisit when/if we make predicates simple rather than classic...

@acl-cqc acl-cqc merged commit 2045c97 into main Jul 25, 2023
6 checks passed
@acl-cqc acl-cqc deleted the refactor/parameterize_typerow_classic_predicate branch July 25, 2023 08:43
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.

2 participants