Skip to content

Conversation

@gmagogsfm
Copy link
Contributor

  • Add EnumType and AnyEnumType as first-class jit type
  • Add Enum-typed IValue
  • Enhanced aten::eq to support Enum

Supported:
Enum-typed function targuments
using Enum type and comparing them

TODO:
Add PyThon sugared value for Enum
Support getting name/value attrs of enums
Support Enum-typed return values
Support enum values of different types in same Enum class
Support serialization and deserialization

@facebook-github-bot facebook-github-bot added the oncall: jit Add this issue/PR to JIT oncall triage queue label Jul 14, 2020
@dr-ci
Copy link

dr-ci bot commented Jul 14, 2020

💊 CI failures summary and remediations

As of commit c642907 (more details on the Dr. CI page):


  • 1/1 failures possibly* introduced in this PR
    • 1/1 non-CircleCI failure(s)

ci.pytorch.org: 1 failed


This comment was automatically generated by Dr. CI (expand for details).Follow this link to opt-out of these comments for your Pull Requests.

Please report bugs/suggestions on the GitHub issue tracker or post in the (internal) Dr. CI Users group.

See how this bot performed.

This comment has been revised 127 times.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@gmagogsfm gmagogsfm marked this pull request as ready for review July 15, 2020 00:26
@gmagogsfm gmagogsfm requested a review from apaszke as a code owner July 15, 2020 00:26
@gmagogsfm gmagogsfm requested a review from eellison July 15, 2020 00:27
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

Looks like a great start!! Just a few comments here or there. Overall looks great though. LMK if you need any pointers for serialization / attribute access.

Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't think of this earlier, but I don't think we should support mutable Enum values. I don't think it's a common use case, and we would have to maintain one singleton IValue.

class Color(Enum):
    RED = torch.tensor(1)

# all Color.RED instances would have to be updated, or we have to maintain a single instance
Color.RED += 2

For the same reason, I don't know if we should support Any because it could be a mutable type (and it's also not a common use case).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, current implementation would break with mutable values. Removed Tensor and Any type.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we force the QualifiedName to always be set ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Added an assert to enforce that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why did we need to make this change ? Seems a little strange

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Enum class doesn't have "__name__" attr, instead they use "name", I am not sure why the inconsistency.

Copy link
Contributor

Choose a reason for hiding this comment

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

Weird. nit: maybe elif isinstance(obj, Enum) in elif conditional instead so it's a little more clear why this is needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed very weird. Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

see comment above about supporting mutable types for enum value

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 AnyType is no longer appropriate. Should I create another type that is super-type of int, float and string for this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should just throw an error instead. If users complain we can get back to this, but the common case is one type of value for an enum.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Done.

Copy link
Contributor Author

@gmagogsfm gmagogsfm left a comment

Choose a reason for hiding this comment

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

Thanks for the suggestions, I have modified accordingly. Please take another look.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, current implementation would break with mutable values. Removed Tensor and Any type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Added an assert to enforce that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Enum class doesn't have "__name__" attr, instead they use "name", I am not sure why the inconsistency.

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 AnyType is no longer appropriate. Should I create another type that is super-type of int, float and string for this case?

@gmagogsfm gmagogsfm requested a review from eellison July 16, 2020 00:37
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

Looks good as far as logic in this PR. I think we should wait to land this until the rest of the stack is implemented, bc otherwise someone could try to use Enums and get half-implemented version (as we have sometimes done with other features :C)

Copy link
Contributor

Choose a reason for hiding this comment

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

Weird. nit: maybe elif isinstance(obj, Enum) in elif conditional instead so it's a little more clear why this is needed

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should just throw an error instead. If users complain we can get back to this, but the common case is one type of value for an enum.

Copy link

@SplitInfinity SplitInfinity left a comment

Choose a reason for hiding this comment

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

Some initial comments

Choose a reason for hiding this comment

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

It seems like there are formatting changes included with your addition below. I recommend undoing the formatting changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I actually intentionally added them. These are all at or near codes that I touched in this PR, in the spirit of leaving the camp ground cleaner. Let me know if this complies with project convention or not.

Choose a reason for hiding this comment

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

I personally prefer to make separate commits to clean up formatting but it's not a big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack. Thanks.

Choose a reason for hiding this comment

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

Nit: undo formatting

Choose a reason for hiding this comment

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

Nit: undo formatting

Choose a reason for hiding this comment

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

Was this intentionally added?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I noticed some of these functions are separated by empty lines but some are not, so made them more consistent.

Choose a reason for hiding this comment

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

Was this intentionally added?

Choose a reason for hiding this comment

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

Consider storing this as a c10::QualifiedName. That class has useful methods for extracting the prefix, unqualified name, etc. that might be useful later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, nice! Done.

Choose a reason for hiding this comment

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

Why is the name allowed to be optional? All enum types must have names, right?

Choose a reason for hiding this comment

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

Also weren't there plans to support heterogeneous enums with mixed value types? Or is that coming later or out of scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Name isn't allowed to be empty. "optional" was originally added to conform to NamedType's convention, but now I realized constructor can take concrete value and then create "optional" to make NamedType happy. Changed.

To answer your second question, Elias gave feedback that heterogeneous enum value types is quite uncommon and makes enum logic a lot more complicated, so we decided to mark it out of scope for now.

Choose a reason for hiding this comment

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

Perhaps this is coming in the future but you should consider adding a test that checks that enums can be returned from scripted functions (i.e. copied back correctly from TS to Python).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely, this is listed as a TODO in the PR description.

Choose a reason for hiding this comment

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

Suggested change
value_type_(value_type), cu_(cu) {
value_type_(std::move(value_type)), cu_(cu) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. But out of curiosity, value_type is already a light-weight TypePtr, does it still benefit from std::move?

Choose a reason for hiding this comment

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

TypePtr is defined as using TypePtr = std::shared_ptr<Type> so I think std::move is a good idea here. Copying it requires atomically incrementing the reference count.

@gmagogsfm
Copy link
Contributor Author

Looks good as far as logic in this PR. I think we should wait to land this until the rest of the stack is implemented, bc otherwise someone could try to use Enums and get half-implemented version (as we have sometimes done with other features :C)

I see your point, but I am a bit concerned about the tsunami of merge conflicts because these PRs combined will touch a lot of files.

How about this? I can hide this functionality behind say a environment variable-based config knob, so that no users would "accidentally" access this feature, and we can avoid merge conflicts.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

I think there'll probably be less merge conflicts than you think, but sure sounds good to land.
Could you add two tests to make sure this is properly guarded:
Something like this fails:

 def enum_comp(x: Color, y: Color) -> bool:
            return x == y
torch.jit.script(enum_comp)

and something like this fails:

class Mod(Module):
      def __init__(self):
            ...
            self.enum_attribute = Color....

      def forward(self, x):
              return self.enum_attribute
torch.jit.script(Mod())

Copy link
Contributor

Choose a reason for hiding this comment

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

AnyEnumType will never show up as an instantiated type, it can go with AnyTupleType::Kind in set of cases

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

It'd be better to use https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/core/jit_type.h#L1513 so that we get a good error message here ("Type str did not match type int...") . you'd need to convert the types to jit type first with ann_to_type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Return None here to disable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a check here, but raised an exception instead of returning None so that user gets a clearer error.

Copy link
Contributor Author

@gmagogsfm gmagogsfm left a comment

Choose a reason for hiding this comment

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

Added a feature guard to prevent users from accidentally using it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a check here, but raised an exception instead of returning None so that user gets a clearer error.

@gmagogsfm gmagogsfm requested a review from eellison July 17, 2020 20:30
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@eellison
Copy link
Contributor

eellison commented Jul 17, 2020

@gmagogsfm not sure if you requested review intentionally, but generally the workflow on pytorch is a reviewer will accept a PR with small outstanding fixes if they think they're easily addressable / nits that dont necessarily have to be addressed.

Can take another look if you'd like but you're good to land without me looking

@gmagogsfm
Copy link
Contributor Author

@gmagogsfm not sure if you requested review intentionally, but generally the workflow on pytorch is a reviewer will accept a PR with small outstanding fixes if they think they're easily addressable / nits that dont necessarily have to be addressed.

Can take another look if you'd like but you're good to land without me looking

Yeah, I clicked requested review to be on the safe side. But now that I think about it, the additional changes are tiny, so I will and now. Thanks eelison and splitinfinity for reviewing.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

* Enum support tempoartily hidden behind environemnt variable EXPERIMENTAL_ENUM_SUPPORT to avoid misuse
* Add EnumType and AnyEnumType as first-class jit type
* Add Enum-typed IValue
* Enhanced aten::eq to support Enum

Supported:
Enum-typed function arguments
using Enum type and comparing them

TODO:
Add PyThon sugared value for Enum
Support getting name/value attrs of enums
Support Enum-typed return values
Support enum values of different types in same Enum class
Support serialization and deserialization
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@gmagogsfm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@gmagogsfm merged this pull request in 4a3aad3.

@gmagogsfm gmagogsfm linked an issue Jul 20, 2020 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Merged oncall: jit Add this issue/PR to JIT oncall triage queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Enum in TorchScript

5 participants