Skip to content

Allow MLGraphBuilder.build() to be called only once - #717

Merged
fdwr merged 2 commits into
webmachinelearning:mainfrom
a-sully:build-once
Jul 16, 2024
Merged

Allow MLGraphBuilder.build() to be called only once#717
fdwr merged 2 commits into
webmachinelearning:mainfrom
a-sully:build-once

Conversation

@a-sully

@a-sully a-sully commented Jul 7, 2024

Copy link
Copy Markdown
Contributor

Fixes #567

If MLGraphBuilder.build() results in anything other than a TypeError - e.g. it successfully resolves with an or MLGraph or rejects with a NotSupportedError - that MLGraphBuilder has now been "built" and all subsequent methods will now reject with an InvalidStateError.


Preview | Diff

Comment thread index.bs
Comment on lines 1404 to 1406
1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=], [=set/append=] |operand| to |inputs|.
1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
1. [=queue/Enqueue=] |input| to |queue|.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note: this PR does not address #567 (comment) since there didn't appear to be consensus on whether that was desirable (FYI @inexorabletash @fdwr). If folks have strong feelings in either direction then I propose we file a separate issue to discuss; otherwise I propose we leave this as-is for now. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SGTM.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

#567 (comment)

Agree it is a separate issue.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Even with this change, we can still AFAICS create a disconnected graph with a single graphBuilder, right? It's a legitimate graph construct.

image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct 👍

@a-sully

a-sully commented Jul 10, 2024

Copy link
Copy Markdown
Contributor Author

@huningxin @fdwr PTAL?

Comment thread index.bs Outdated
Comment thread index.bs Outdated
Comment thread index.bs
Comment on lines 1404 to 1406
1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=], [=set/append=] |operand| to |inputs|.
1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
1. [=queue/Enqueue=] |input| to |queue|.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SGTM.

Comment thread index.bs

@huningxin huningxin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM with a comment

Checked with @Honry that ORT WebNN EP currently doesn't share constant operands when building sub-graphs for a (merged) model.

Comment thread index.bs
Comment thread index.bs
Comment on lines 1404 to 1406
1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=], [=set/append=] |operand| to |inputs|.
1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
1. [=queue/Enqueue=] |input| to |queue|.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

#567 (comment)

Agree it is a separate issue.

Comment thread index.bs
Comment on lines 1404 to 1406
1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=], [=set/append=] |operand| to |inputs|.
1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
1. [=queue/Enqueue=] |input| to |queue|.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Even with this change, we can still AFAICS create a disconnected graph with a single graphBuilder, right? It's a legitimate graph construct.

image

Comment thread index.bs
<summary>
The <dfn method for=MLGraphBuilder>where(|condition|, |input|, |other|)</dfn> method steps are:
</summary>
1. If [=this=].{{MLGraphBuilder/[[hasBuilt]]}} is true, then [=exception/throw=] an "{{InvalidStateError}}" {{DOMException}}.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🫤 Seeing "if hasBuilt is true then throw this kind of error" repeated in the logic of every single operator feels unclean. Can we have a common graph builder "operator initialization" function that they all call, and that error check can be the very first (and currently only) step in it, kinda like how we centralized casting and broadcasting functionality? Mind you, every single operator still then needs "1. Perform builder-operator-initialization", but it feels 🧼➕ imo. Opinion?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm I agree the current state is annoyingly duplicative, but I couldn't decide on a useful name of a helper method... This check is performed in MLGraphBuilder.build() as well all methods which mint an MLOperand. Calling a "builder-operator-initialization" algorithm from the build() steps is a bit odd (and nothing is being initialized anyways).

Lacking a good name, I figured inlining the one-liner would be more readable ¯\(ツ)

If you can think of a better name then I'm happy to change it :P

@fdwr fdwr Jul 11, 2024

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The pervasive naming challenge... Let me ponder a bit, after some sleep. 😴

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Throwing out another idea: we could maybe use a Bikeshed text macro to reduce the burden on spec authors. We define one today for EMULATED to avoid repetition. This doesn't reduce the burden on spec readers, though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

True, though we'd still have to name the macro :P

THROW_IF_BUILT?

@fdwr fdwr Jul 16, 2024

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

THROW_IF_ALREADY_BUILT?
EnsureNotAlreadyBuilt?
...
No particularly bright insights came to me after sleep or over the weekend 😅. Going to merge now and maybe we'll think of something later...

@fdwr fdwr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍 The builder patterns I've seen generally let you create multiple objects from the same builder, but I see advantages to both, and it's easier to potentially relax this later if needed than to restrict it later.

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.

Can an MLGraphBuilder be reused?

4 participants