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

Add the clamp() algorithm #348

Closed
wants to merge 3 commits into from

Conversation

zolkis
Copy link
Collaborator

@zolkis zolkis commented Feb 15, 2023

Add the clamp algorithm. Tracked in #347 (discussion there).
Depends on #336.


Preview | Diff

@zolkis
Copy link
Collaborator Author

zolkis commented Feb 16, 2023

During the call today, @huningxin you made a remark that an MLActivation is not needed when an MLOperator is returned. To me it seems it was needed in the impl, also for the step of connecting the op input/output. But welcome to suggest better steps here, thanks!

@huningxin
Copy link
Contributor

@zolkis

To me it seems it was needed in the impl, also for the step of connecting the op input/output. But welcome to suggest better steps here, thanks!

The impl you referred to returns an MLOperand.

This impl returns an MLOperator (and should be updated to MLActivation)

@zolkis
Copy link
Collaborator Author

zolkis commented Feb 23, 2023

@zolkis

To me it seems it was needed in the impl, also for the step of connecting the op input/output. But welcome to suggest better steps here, thanks!

The impl you referred to returns an MLOperand.

This impl returns an MLOperator (and should be updated to MLActivation)

We have a single algorithm for clamp() in JS, and we distinguish for the return values based on the arguments provided to the function. So the two impl need to be combined there. Of course in cpp the implementation can be polymorphic. AFAIK we cannot have 2 separate algorithms mirroring a cpp implementation. We need to figure out what we want in JS, and then implement in cpp. As this is the same case with almost every other function in the API, we need to get this right.
I added a note to clarify the algorithm.

@zolkis
Copy link
Collaborator Author

zolkis commented Feb 23, 2023

Also, I will need to update #337 with internal constructors for MLOperand and MLActivation as needed in the steps of the clamp() algorithm. I think it's better to add those commits to that PR, not this one.

@zolkis
Copy link
Collaborator Author

zolkis commented Mar 18, 2023

I modified the algorithm like they would be when #337 is merged. This shows how the create MLOperand and create MLActivation steps would be used.

@zolkis zolkis changed the title [WiP] Add the clamp() algorithm Add the clamp() algorithm Mar 19, 2023
@zolkis
Copy link
Collaborator Author

zolkis commented Mar 21, 2023

I am preparing the PR for input() and constant(), and that changes this PR, too. Moving it back to WiP.

@zolkis zolkis changed the title Add the clamp() algorithm WiP: Add the clamp() algorithm Mar 21, 2023
index.bs Outdated
The {{MLGraphBuilder/clamp()}} method steps are:
1. Let |arg1| be the first argument.
1. If |arg1| is an instance of {{MLOperand}}, run the {{MLGraphBuilder/clamp(operand, options)}} steps with all the received arguments.
1. Otherwise, run the {{MLGraphBuilder/clamp(options)}} steps with all received arguments.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@huningxin : in general, for polymorphic functions, do we prefer adding these boilerplate lines, or just define the two forms separately?

index.bs Outdated
Comment on lines 1288 to 1290
<div class="note">
This step does not execute the clamp operation yet, it just prepares the operator.
</div>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this note is needed because the same statement can also be made for all the other graph builder methods that add a given operation into the graph i.e. nothing is executed yet.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed it would be better to state that once for all the ops and remove the repetitive notes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed.

@zolkis zolkis changed the title WiP: Add the clamp() algorithm Add the clamp() algorithm Apr 13, 2023
@zolkis
Copy link
Collaborator Author

zolkis commented Apr 20, 2023

@wchao1115 concern addressed, PTAL.

@zolkis zolkis force-pushed the clamp-algorithm branch from af38f15 to 3f81f3a Compare May 16, 2023 15:48
@zolkis
Copy link
Collaborator Author

zolkis commented May 16, 2023

@huningxin @wchao1115 PTAL: squashed to one commit.

@anssiko anssiko requested a review from huningxin May 24, 2023 06:28
zolkis added 2 commits May 25, 2023 19:03
Squashed from the following commits:
    Replace MLOperand.[[descriptor]] with type and dimensions
    Clarify the algorithm for only setting up the op
    Improve the clamp() algorithm, use the prose assuming the create steps for MLOperand and MLActivation
    Rework clamp with polymorphic behavior. Update for changes in MLOperand.
    Rework clamp() like constant(), polymorphic forms in separate sections, argument and return descriptions as notes.
    Fix platform related steps and reference to internal slots
    Address review, remove note
    Remove back quotes from title

Signed-off-by: Zoltan Kis <[email protected]>
@zolkis zolkis force-pushed the clamp-algorithm branch from 3f81f3a to d68bd87 Compare May 25, 2023 16:13
1. Set |activation|.{{MLActivation/[[builder]]}} to |builder|.
1. Set |activation|.{{MLActivation/[[name]]}} to |name|.
1. If |options| is an [=object=], set |activation|.{{MLActivation/[[options]]}} to |options|.
1. Make a request to the underlying platform to bind the [=implementation-defined=] platform operator for |name| to |activation|.{{MLActivation/[[operator]]}}.
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 leave the platform operator creation to each build method? Because I suppose the creation steps would be different, e.g. handling of options.

Copy link
Collaborator Author

@zolkis zolkis Jun 1, 2023

Choose a reason for hiding this comment

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

I agree. Let's start with that assumption. We can always cleanly factor out common things when the opportunity comes.

1. If |operand| is not an instance of {{MLOperand}}, then throw a "{{TypeError}}" and stop.
1. Let |result| be a new [=object=].
1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}.
1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}.
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 do deep copy of the descriptor? Would the change of original operand's descriptor impact the copied one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All internal slots should be immutable, i.e. only changeable with API calls, so assignments to internal slots should involve deep copies by default. I will check this.

Copy link
Collaborator Author

@zolkis zolkis Jun 2, 2023

Choose a reason for hiding this comment

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

ECMAScript doesn't really shed light on this. Internal slots are supposed to be created and then not modified unless specified differently.

Now we seem to have 2 options:

  1. change every line that sets an internal slot to replace the argument with "a copy of the argument"
  2. or, create steps to "set the internal slot [[name]] to <arg>" steps, where we say "set [[name]] to a copy of <arg>".

@anssiko @dontcallmedom any input here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@huningxin
Since this is a general issue for the spec, could we create an issue and fix it outside this PR, in all affected places?

Copy link
Contributor

Choose a reason for hiding this comment

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

I am fine to fix it in a separate issue. Please create that issue and add link into the spec. Thanks.

index.bs Outdated
1. If running the <a>check clamp options</a> steps with |options| returns `false`, then throw a "{{TypeError}}" {{DOMException}} and abort these steps.
1. Let |result| be the result of invoking the <a>copy MLOperand</a> steps given |operand|.
1. If that throws an error, re-throw the error and abort these steps.
1. Make a request to the underlying platform to connect |result| with the [=implementation-defined=] platform operator for clamp, and store a reference to the resulting [=implementation-defined=] platform operand object in |result|.{{MLOperand/[[operand]]}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose there are a few sub-steps:

  1. Make a request to the underlying platform to create an [=implementation-defined=] platform operator for clamp with |options|.{{MLClampOptions/minValue}} and |options|.{{MLClampOptions/minValue}}.
  2. Register the |operand|.{{MLOperand/[[operand]]}} as input to the platform operator for clamp.
  3. Register the |result|.{{MLOperand/[[operand]]}} as output to the platform operator for clamp.
  4. Store a reference to the platform operator for clamp in |result|.{{MLOperand/[[operator]]}}.

index.bs Outdated
1. If running the <a>check clamp options</a> steps with |options| returns `false`, then throw a "{{TypeError}}" {{DOMException}} and abort these steps.
1. Let |op| be the result of invoking the <a>create MLActivation</a> steps with `"clamp"` and |options|.
1. If that throws an error, re-throw the error and abort these steps.
1. Make a request to the underlying platform to connect |op| with the [=implementation-defined=] platform operator for clamp, and store a reference to the resulting [=implementation-defined=] platform operator object in |op|.{{MLActivation/[[operator]]}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose we can leverage the steps of {{MLGraphBuilder/clamp(operand, options)}} method. The difference is here it doesn't need register input and output operand.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is for activation and let's try its own steps. Let's keep track if we could reuse these operand and activation platform-related steps in other methods and how to parametrize them. If there is an opportunity, we can factor them out later.

index.bs Outdated
1. Let |result| be a new [=object=].
1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}.
1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}.
1. Set |result|.{{MLOperand/[[name]]}} to |operand|.{{MLOperand/[[name]]}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

{{MLOperand/[[name]]}} is only valid for input operand, it should not be copied if the destination operand is not an input.

- an {{MLOperand}}. The output tensor of the same shape as *operand*.
</div>
<div algorithm=clamp>
The {{MLGraphBuilder/clamp(operand, options)}} method steps are:
Copy link
Contributor

Choose a reason for hiding this comment

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

operand is commonly used, e.g. for MLOperand or platform operand etc. Should the variable name be more specific, like input or x?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, so that was the reason for using x. Well, input is also a loaded name :).
Since it is an MLOperand, I think using operand is just fine.

@zolkis zolkis requested a review from huningxin June 2, 2023 14:27
Copy link
Contributor

@huningxin huningxin left a comment

Choose a reason for hiding this comment

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

LGTM with a follow-up for "deep-copy" issue.

1. If |operand| is not an instance of {{MLOperand}}, then throw a "{{TypeError}}" and stop.
1. Let |result| be a new [=object=].
1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}.
1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

I am fine to fix it in a separate issue. Please create that issue and add link into the spec. Thanks.

@zolkis
Copy link
Collaborator Author

zolkis commented Jun 13, 2023

Moved to #399 with new target branch.

@zolkis zolkis closed this Jun 13, 2023
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.

3 participants