Web Neural Network API

W3C Candidate Recommendation Draft,

More details about this document
This version:
https://www.w3.org/TR/2024/CRD-webnn-20240203/
Latest published version:
https://www.w3.org/TR/webnn/
Editor's Draft:
https://webmachinelearning.github.io/webnn/
Previous Versions:
History:
https://www.w3.org/standards/history/webnn/
Implementation Report:
https://wpt.fyi/results/webnn?label=master&label=experimental&aligned&q=webnn
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/webnn
Feedback:
GitHub
Inline In Spec
Editors:
Ningxin Hu (Intel Corporation)
Chai Chaoweeraprasit (Microsoft Corporation)
Explainer:
explainer.md
Polyfill:
webnn-polyfill / webnn-samples

Abstract

This document describes a dedicated low-level API for neural network inference hardware acceleration.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document was published by the Web Machine Learning Working Group as a Candidate Recommendation Draft using the Recommendation track.

Publication as a Candidate Recommendation does not imply endorsement by W3C and its Members. A Candidate Recommendation Draft integrates changes from the previous Candidate Recommendation that the Working Group intends to include in a subsequent Candidate Recommendation Snapshot.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

The Web Machine Learning Working Group maintains a list of all bug reports that the group has not yet addressed. Pull requests with proposed specification text for outstanding issues are strongly encouraged.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

Further implementation experience and user feedback is being gathered for the MLCommandEncoder interface that proposes to enable more efficient WebGPU integration. A proposal to simplify MLContext creation is being discussed. This document is maintained and updated at any time. Some parts of this document are work in progress and further improvements are expected to be reflected in revised Candidate Recommendation Drafts and Snaphots.

Before requesting transition to Proposed Recommendation, the Working Group will seek to demonstrate that:

1. Introduction

The Web Neural Network API defines a web-friendly hardware-agnostic abstraction layer that makes use of Machine Learning capabilities of operating systems and underlying hardware platforms without being tied to platform-specific capabilities. The abstraction layer addresses the requirements of key Machine Learning JavaScript frameworks and also allows web developers familiar with the ML domain to write custom code without the help of libraries.

For an illustrated introduction, please see the explainer.

2. Use cases

2.1. Application Use Cases

This section illustrates application-level use cases for neural network inference hardware acceleration. All applications in those use cases can be built on top of pre-trained deep neural network (DNN) [models].

Note: Please be aware that some of the use cases described here, are by their very nature, privacy-invasive. Developers who are planning to use the API for such use cases should ensure that the API is being used to benefit users, for purposes that users understand, and approve. They should apply the Ethical Principles for Web Machine Learning [webmachinelearning-ethics] and implement appropriate privacy risk mitigations such as transparency, data minimisation, and users controls.

2.1.1. Person Detection

A user opens a web-based video conferencing application, but she temporarily leaves from her room. The application is watching whether she is in front of her PC by using object detection (for example, using object detection approaches such as [SSD] or [YOLO] that use a single DNN) to detect regions in a camera input frame that include persons.

When she comes back, the application automatically detects her and notifies other online users that she is active now.

2.1.2. Semantic Segmentation

A user joins a teleconference via a web-based video conferencing application at her desk since no meeting room in her office is available. During the teleconference, she does not wish that her room and people in the background are visible. To protect the privacy of the other people and the surroundings, the application runs a machine learning model such as [DeepLabv3+], [MaskR-CNN] or [SegAny] to semantically split an image into segments and replaces segments that represent other people and background with another picture.

2.1.3. Skeleton Detection

A web-based video conferencing application tracks a pose of user’s skeleton by running a machine learning model, which allows for real-time human pose estimation, such as [PoseNet] to recognize her gesture and body language. When she raises her hand, her microphone is automatically unmuted and she can start speaking on the teleconference.

2.1.4. Face Recognition

There are multiple people in the conference room and they join an online meeting using a web-based video conferencing application. The application detects faces of participants by using object detection (for example, using object detection approaches such as [SSD]) and checks whether each face was present at the previous meeting or not by running a machine learning model such as [FaceNet], which verifies whether two faces would be identical or not.

2.1.5. Facial Landmark Detection

A user wants to find new glasses that beautifully fits her on an online glasses store. The online store offers web-based try-on simulator that runs a machine learning model such as Face Alignment Network [FAN] to detect facial landmarks like eyes, nose, mouth, etc. When she chooses a pair of glasses, the simulator properly renders the selected glasses on the detected position of eyes on her facial image.

2.1.6. Style Transfer

A user is looking for cosmetics on an online store and wondering which color may fit her face. The online store shows sample facial makeup images of cosmetics, and offers makeup simulator that runs a machine learning model like [ContextualLoss] or [PairedCycleGAN] to transfer the makeup style of the sample makeup image to her facial image. She can check how the selected makeup looks like on her face by the simulator.

2.1.7. Super Resolution

A web-based video conferencing is receiving a video stream from its peer, but the resolution of the video becomes lower due to network congestion. To prevent degradation of the perceived video quality, the application runs a machine learning model for super-resolution such as [SRGAN] to generate higher-resolution video frames.

2.1.8. Image Captioning

For better accessibility, a web-based presentation application provides automatic image captioning by running a machine learning model such as [im2txt] which predicts explanatory words of the presentation slides.

2.1.9. Text-to-image

Images are a core part of modern web experiences. An ability to generate images based on text input in a privacy-preserving manner enables visual personalization and adaptation of web applications and content. For example, a web application can use as an input a natural language description on the web page or a description provided by the user within a text prompt to produce an image matching the text description. This text-to-image use case enabled by latent diffusion model architecture [LDM] forms the basis for additional text-to-image use cases. For example, inpainting where a portion of an existing image on the web page is selectively modified using the newly generated content, or the converse, outpainting, where an original image is extended beyond its original dimensions filling the empty space with generated content.

2.1.10. Machine Translation

Multiple people from various countries are talking via a web-based real-time text chat application. The application translates their conversation by using a machine learning model such as [GNMT] or [OpenNMT], which translates every text into different language.

2.1.11. Emotion Analysis

A user is talking to her friend via a web-based real-time text chat application, and she is wondering how the friend feels because she cannot see the friend’s face. The application analyses the friend’s emotion by using a machine learning model such as [DeepMoji], which infers emotion from input texts, and displays an emoji that represents the estimated emotion.

2.1.12. Video Summarization

A web-based video conferencing application records received video streams, and it needs to reduce recorded video data to be stored. The application generates the short version of the recorded video by using a machine learning model for video summarization such as [Video-Summarization-with-LSTM].

2.1.13. Noise Suppression

A web-based video conferencing application records received audio streams, but usually the background noise is everywhere. The application leverages real-time noise suppression using Recurrent Neural Network such as [RNNoise] for suppressing background dynamic noise like baby cry or dog barking to improve audio experiences in video conferences.

2.1.14. Speech Recognition

Speech recognition, also known as speech to text, enables recognition and translation of spoken language into text. Example applications of speech recognition include transcription, automatic translation, multimodal interaction, real-time captioning and virtual assistants. Speech recognition improves accessibility of auditory content and makes it possible to interact with such content in a privacy-preserving manner in a textual form. Examples of common use cases include watching videos or participating in online meetings using real-time captioning. Models such as [Whisper] approach humans in their accuracy and robustness and are well positioned to improve accessibility of such use cases.

2.1.15. Text Generation

Various text generation use cases are enabled by large language models (LLM) that are able to perform tasks where a general ability to predict the next item in a text sequence is required. This class of models can translate texts, answer questions based on a text input, summarize a larger body of text, or generate text output based on a textual input. LLMs enable better performance compared to older models based on RNN, CNN, or LSTM architectures and further improve the performance of many other use cases discussed in this section. Examples of LLMs include [t5-small], [m2m100_418M], [gpt2], and [llama-2-7b].

2.1.16. Detecting fake video

A user is exposed to realistic fake videos generated by ‘deepfake’ on the web. The fake video can swap the speaker’s face into the president’s face to incite a user politically or to manipulate user’s opinion. The deepfake detection applications such as [FaceForensics++] analyze the videos and protect a user against the fake videos or images. When she watches a fake video on the web, the detection application alerts her of the fraud video in real-time.

2.2. Framework Use Cases

This section collects framework-level use cases for a dedicated low-level API for neural network inference hardware acceleration. It is expected that Machine Learning frameworks will be key consumers of the Web Neural Network API (WebNN API) and the low-level details exposed through the WebNN API are abstracted out from typical web developers. However, it is also expected that web developers with specific interest and competence in Machine Learning will want to interface with the WebNN API directly instead of a higher-level ML framework.

2.2.1. Custom Layer

A web application developer wants to run a DNN model on the WebNN API. However, she has found that some of activation functions like [LeakyReLU], [ELU], etc. are not included in the WebNN API. To address this issue, she constructs custom layers of the additional activation functions on top of the WebNN API. Note that the scope of custom layers may include convolution, normalization, etc. as well as activation.

2.2.2. Network Concatenation

A web application uses a DNN model, and its model data of upper convolutional layers and lower fully-connected layers are stored in separate files, since model data of the fully-connected layers are periodically updated due to fine tuning at the server side.

Therefore, the application downloads both partial model files at first and concatenates them into a single model. When the model is updated, the application downloads fine-tuned part of the model and replace only the fully-connected layers with it.

2.2.3. Performance Adaptation

A web application developer has a concern about performance of her DNN model on mobile devices. She has confirmed that it may run too slow on mobile devices which do not have GPU acceleration. To address this issue, her web application refers to the WebNN API to confirm whether acceleration is available or not, so that the application can display the warning for devices without acceleration.

After several weeks, she has developed a tiny DNN model that can even run on CPU. In order to accommodate CPU execution, she modifies the application so that the application loads the tiny model in the case of CPU-only devices.

2.2.4. Operation Level Execution

A JavaScript ML framework is responsible for loading, interpreting and executing a ML model. During the model execution phase, the framework iterates through the operations of the model and executes each operation on the hardware device, like CPU, GPU or ML accelerator. To avoid the unnecessary data copying across devices, the framework selects the same device to execute the operations. For a compute intensive operation, such as convolution 2D or matrix multiplication, the framework uses WebNN API to execute it with the ML-specific acceleration available on that selected device.

2.2.5. Integration with real-time video processing

The user experience of WebRTC-based video conferencing is enhanced using real-time video processing. For example, background blur implemented using a § 2.1.2 Semantic Segmentation model blurs the background in the user’s live camera feed. To satisfy the performance requirements of this use case, the WebNN API integrates with primitives from other Web APIs that make up the media pipeline to allow WebNN API-based transformation of real-time video streams.

3. Security Considerations

This specification defines a low-level API for neural network inference hardware acceleration. This API is considered a powerful feature [POWERFUL-FEATURES] because it grants low-level access to a user’s computer. To meet the authentication and confidentiality expectations of a powerful feature and to prevent man-in-the-middle attacks, all interfaces defined by this specification are only available in a secure context.

This API is disabled by default in all cross-origin frames using the § 7.2.1 Permissions Policy Integration. This prevents third-party content from using this API unless the embedding page explicitly sets a policy that grants permission.

This API allows creation of an MLContext from a GPUDevice defined by WebGPU specification. See WebGPU Security Considerations for more information regarding security characteristics of this context.

Once the graph is fully constructed and compiled, the input shapes into each of the operations in the graph are inferred and finalized. The bounds checking occurs when the compute method is invoked that executes the graph against the actual data. No actual data is bound to the compiled graph before this stage. It is the implementation’s responsibility to make sure proper bounds checking occurs against the shapes of the data already inferred by that time.

Document operations susceptible to out-of-bounds access as a guidance to implementers.

As a future-proofing measure, the API design allows certain operations that can be generically emulated to be deprecated for security, performance, or other reasons without breaking compatibility. This is made possible by high-level functions that are defined in terms of smaller primitive operations defined in this specifications. This enables a native implementation of a high-level function to be replaced with a polyfill implementation.

Investigate side channel attack feasibility considering the current state where CPU is shared between processes running renderers.

In order to not allow an attacker to target a specific implementation that may contain a flaw, the § 6.2 Device Selection mechanism is a hint only, and the concrete device selection is left to the implementation - a user agent could for instance choose never to run a model on a device with known vulnerabilities. As a further mitigation, no device enumeration mechanism is defined.

Hinting partially mitigates the concern. Investigate additional mitigations.

The API design minimizes the attack surface for the compiled computational graph. The MLGraphBuilder interface that hosts the various operations is a data definition API and as such doesn’t execute anything, only constructs data. What follows, is that the potential for an attack is limited to when binding the data to the graph before executing it by invoking the MLContext.compute() method. This enables implementers to focus on hardening the MLContext.compute() method. For example, by making sure it honors the boundary of data and fails appropriately when the bounds are not respected.

Purpose-built Web APIs for measuring high-resolution time mitigate against timing attacks using techniques such as resolution reduction, adding jitter, detection of abuse and API call throttling [hr-time-3]. The practical deployment of WebNN implementations are likely to bring enough jitter to make timing attacks impractical (e.g. because they would use IPC) but implementers are advised to consider and test their implementations against timing attacks.

3.1. Guidelines for new operations

To ensure operations defined in this specification are shaped in a way they can be implemented securely, this section includes guidelines on how operations are expected to be defined to reduce potential for implementation problems. These guidelines are expected to evolve over time to align with industry best practices:

In general, always consider the security and privacy implications as documented in [security-privacy-questionnaire] by the Technical Architecture Group and the Privacy Interest Group when adding new features.

4. Privacy Considerations

This API enhances privacy compared to cloud-based inference, since input data such as locally sourced images or video streams stay within the browser’s sandbox.

This API exposes the minimum amount of information necessary to address the identified § 2 Use cases for the best performance and reliability of results.

No information from the underlying platform is exposed directly. An execution time analysis may reveal indirectly the performance of the underlying platform’s neural network hardware acceleration capabilities relative to another underlying platform.

Note: The group is soliciting further input on the proposed execution time analysis fingerprinting vector and will augment this section with more information and mitigations to inform the implementers of this API.

Unlike WebGPU, this API does not intrinsically support custom shader authoring; and as a result is not prone to timing attacks that rely on shader caches, or other persistent data. The API builds upon pre-existing shaders and lower level primitives of the browser or the underlying OS. Web developers who interface with GPUDevice are expected to be aware of WebGPU compilation cache considerations.

The WebGPU API identifies machine-specific artifacts as a privacy consideration. Given the WebNN API defines means to record an ML workload onto a WebGPU-compatible GPUCommandBuffer, compute unit scheduling may under certain circumstances introduce a fingerprint. However, similarly to WebGPU, such fingerprints are identical across most or all of the devices of each vendor, mitigating the concern. Furthermore, software implementations can be used to further eliminate such artifacts.

The WebNN API defines two developer-settable preferences to help inform § 6.2 Device Selection and allow the implementation to better select the most appropriate underlying execution device for the workload. Device type normatively indicates the kind of device and is either "cpu" or "gpu". If this type cannot be satisfied, an "OperationError" DOMException is thrown, thus this type can in some cases add two bits of entropy to the fingerprint. Power preference indicates preference as related to the power consumption and is considered a hint only and as such does not increase entropy of the fingerprint.

If a future version of this specification introduces support for new a device type that can only support a subset of MLOperandDataTypes, that may introduce a new fingerprint.

In general, implementers of this API are expected to apply WebGPU Privacy Considerations to their implementations where applicable.

5. Ethical Considerations

The Working Group has started documenting ethical issues associated with using Machine Learning on the Web, to help identify what mitigations its normative specifications should take into account. The Working Group publishes and maintains an Ethical Principles for Web Machine Learning document [webmachinelearning-ethics] open to contributions from the wider community via a dedicated GitHub repository.

6. Programming Model

6.1. Overview

At the heart of neural networks is a computational graph of mathematical operations. These operations are the building blocks of modern machine learning technologies in computer vision, natural language processing, and robotics. The WebNN API is a specification for constructing, compiling, and executing computational graphs of neural networks.

The MLGraph interface represents a compiled computational graph that is immutable (that is, a model).

The MLGraphBuilder interface serves as a builder (factory) to create an MLGraph. An MLOperand is a representation of data that flows within the computational graph, which include input-values for inference, constants (including trained weights) used for inference, intermediate values (often referred to as activations) computed during inference, as well as the output values of inference. At inference time, every MLOperand will be bound to a tensor (the actual data).

The MLGraphBuilder interface enables the creation of MLOperands. A key part of the MLGraphBuilder interface are the operations (such as MLGraphBuilder.gemm() and MLGraphBuilder.softmax()). The operations have a functional semantics, with no side effects. Each operation invocation conceptually returns a distinct new value, without changing the value of any other MLOperand.

Internally, the MLGraphBuilder methods such as gemm() create an implementation-defined platform operator which is held by the MLOperand or MLActivation, which performs the actual operation on the input data when the computation is run. An MLOperand also holds an implementation-defined platform operand, which references the operand in the underlying computational graph, and is connected to platform operators as input and/or output.

The runtime values (of MLOperands) are tensors, which are essentially multidimensional arrays. The representation of the tensors is implementation dependent, but it typically includes the array data stored in some buffer (memory) and some metadata describing the array data (such as its shape).

As mentioned above, the operations have functional semantics. This allows the implementation to potentially share the array data between multiple tensors. For example, the implementation of operations such as reshape, or slice may return a view of its input tensor that shares the same buffer as the input tensor. (In the case of reshape, the entire data is shared, while in the case of slice, a part of the input data is shared.) The implementation may use views, as above, for intermediate values.

Before the execution, the computation graph that is used to compute one or more specified outputs needs to be compiled and optimized. The key purpose of the compilation step is to enable optimizations that span two or more operations, such as operation or loop fusion.

There are multiple ways by which the graph may be compiled. The MLGraphBuilder.build() method compiles the graph in the background without blocking the calling thread, and returns a Promise that resolves to an MLGraph. The MLGraphBuilder.buildSync() method compiles the graph immediately on the calling thread, which must be a worker thread running on CPU or GPU device, and returns an MLGraph. Both compilation methods produce an MLGraph that represents a compiled graph for optimal execution.

Once the MLGraph is constructed, there are multiple ways by which the graph may be executed. The MLContext.computeSync() method represents a way the execution of the graph is carried out immediately on the calling thread, which must also be a worker thread, either on a CPU or GPU device. The execution produces the results of the computation from all the inputs bound to the graph.

The MLContext.compute() method represents a way the execution of the graph is performed asynchronously either on a parallel timeline in a separate worker thread for the CPU execution or on a GPU timeline in a GPU command queue. This method returns immediately without blocking the calling thread while the actual execution is offloaded to a different timeline. This type of execution is appropriate when the responsiveness of the calling thread is critical to good user experience. The computation results will be placed at the bound outputs at the time the operation is successfully completed on the offloaded timeline at which time the calling thread is signaled. This type of execution supports both the CPU and GPU device.

In both the MLContext.compute() and MLContext.computeSync() execution methods, the caller supplies the input values using MLNamedArrayBufferViews, binding the input MLOperands to their values. The caller then supplies pre-allocated buffers for output MLOperands using MLNamedArrayBufferViews.

The MLCommandEncoder interface created by the MLContext.createCommandEncoder() method supports a graph execution method that provides the maximum flexibility to callers that also utilize WebGPU in their application. It does this by placing the workload required to initialize and compute the results of the operations in the graph onto a GPUCommandBuffer. The callers are responsible for the eventual submission of this workload on the GPUQueue through the WebGPU queue submission mechanism. Once the submitted workload is completely executed, the result is avaialble in the bound output buffers.

6.2. Device Selection

An MLContext interface represents a global state of neural network execution. One of the important context states is the underlying execution device that manages the resources and facilitates the compilation and the eventual execution of the neural network graph. In addition to the default method of creation with MLContextOptions, an MLContext could also be created from a specific GPUDevice that is already in use by the application, in which case the corresponding GPUBuffer resources used as graph constants, as well as the GPUTexture as graph inputs must also be created from the same device. In a multi-adapter configuration, the device used for MLContext must be created from the same adapter as the device used to allocate the resources referenced in the graph.

In a situation when a GPU context executes a graph with a constant or an input in the system memory as an ArrayBufferView, the input content is automatically uploaded from the system memory to the GPU memory, and downloaded back to the system memory of an ArrayBufferView output buffer at the end of the graph execution. This data upload and download cycles will only occur whenever the execution device requires the data to be copied out of and back into the system memory, such as in the case of the GPU. It doesn’t occur when the device is a CPU device. Additionally, the result of the graph execution is in a known layout format. While the execution may be optimized for a native memory access pattern in an intermediate result within the graph, the output of the last operation of the graph must convert the content back to a known layout format at the end of the graph in order to maintain the expected behavior from the caller’s perspective.

When an MLContext is created with MLContextOptions, the user agent selects and creates the underlying execution device by taking into account the application’s power preference and device type specified in the MLPowerPreference and MLDeviceType options.

The following table summarizes the types of resource supported by the context created through different method of creation:

Creation method ArrayBufferView GPUBuffer GPUTexture
MLContextOptions Yes No No
GPUDevice Yes Yes Yes

7. API

7.1. The navigator.ml interface

An ML object is available in the Window and DedicatedWorkerGlobalScope contexts through the Navigator and WorkerNavigator interfaces respectively and is exposed via navigator.ml.

interface mixin NavigatorML {
  [SecureContext, SameObject] readonly attribute ML ml;
};
Navigator includes NavigatorML;
WorkerNavigator includes NavigatorML;

7.2. ML interface

enum MLDeviceType {
  "cpu",
  "gpu"
};

enum MLPowerPreference {
  "default",
  "high-performance",
  "low-power"
};

dictionary MLContextOptions {
  MLDeviceType deviceType = "cpu";
  MLPowerPreference powerPreference = "default";
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface ML {
  Promise<MLContext> createContext(optional MLContextOptions options = {});
  Promise<MLContext> createContext(GPUDevice gpuDevice);

  [Exposed=(DedicatedWorker)]
  MLContext createContextSync(optional MLContextOptions options = {});
  [Exposed=(DedicatedWorker)]
  MLContext createContextSync(GPUDevice gpuDevice);
};

7.2.1. Permissions Policy Integration

This specification defines a policy-controlled feature identified by the string "webnn". Its default allowlist is 'self'.

7.2.2. createContext

To create a context given options, run these steps:
  1. Let context be a new MLContext object.

  2. If options is a GPUDevice object,

    1. Set context.[[contextType]] to "webgpu".

    2. Set context.[[deviceType]] to "gpu".

    3. Set context.[[powerPreference]] to "default".

  3. Otherwise,

    1. Set context.[[contextType]] to "default".

    2. If options["deviceType"] exists, then set context.[[deviceType]] to options["deviceType"]. Otherwise, set context.[[deviceType]] to "cpu".

    3. If options["powerPreference"] exists, then set context.[[powerPreference]] to options["powerPreference"]. Otherwise, set context.[[powerPreference]] to "default".

  4. If the user agent cannot support context.[[contextType]], context.[[deviceType]] and context.[[powerPreference]], return failure.

  5. Return context.

The createContext(options) steps are:
  1. If this's relevant global object's associated Document is not allowed to use the webnn feature, return a new promise rejected with a "SecurityError" DOMException.

  2. Let promise be a new promise.

  3. Return promise and run the following steps in parallel.

  4. Let context be the result of creating a context given options. If that returns failure, then reject promise with a "NotSupportedError" DOMException.

  5. Resolve promise with context.

The createContext(gpuDevice) method steps are:
  1. If this's relevant global object's associated Document is not allowed to use the webnn feature, return a new promise rejected with a "SecurityError" DOMException.

  2. Let promise be a new promise.

  3. Return promise and run the following steps in parallel.

  4. Let context be the result of creating a context given gpuDevice. If that returns failure, reject promise with a "NotSupportedError" DOMException.

  5. Resolve promise with context.

7.2.3. createContextSync

The createContextSync(options) method steps are:
  1. If this's relevant global object's associated Document is not allowed to use the webnn feature, then throw a "SecurityError" DOMException.

  2. Let context be the result of creating a context with options. If that returns failure, then throw a "NotSupportedError" DOMException.

  3. Return context.

The createContextSync(gpuDevice) method steps are:
  1. If this's relevant global object's associated Document is not allowed to use the webnn feature, then throw a "SecurityError" DOMException.

  2. Let context be the result of creating a context with gpuDevice. If that returns failure, then throw a "NotSupportedError" DOMException.

  3. Return context.

7.3. MLActivation interface

Objects implementing the MLActivation interface represent activation function types.

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLActivation {
};
MLActivation has the following internal slots:
[[name]] of type string

The MLActivation's name.

[[builder]] of type MLGraphBuilder

The graph builder object this MLActivation belongs to.

[[options]] of type ordered map

A dictionary containing MLActivation options.

[[operator]] of type platform operator

Reference to MLActivation's corresponding platform operator.

These activations function types are used to create other operations. One such use of this interface is for when an activation function is fused into another operation such as § 7.7.9 conv2d or § 7.7.3 batchNormalization during a graph construction session. Such fused activation functions can provide a significant performance improvement when supported natively by the underlying implementation. This is intended as an optimization opportunity for implementers.

7.3.1. Creating MLActivation

The MLActivation objects (including the ones passed as input to methods) are created by the methods of MLGraphBuilder and are identified by their name. The options dictionary is defined by those methods. The actual creation of the activation function e.g. a § 7.7.37 sigmoid or § 7.7.34 relu can then be deferred until when the rest of the graph is ready to connect with it such as during the construction of § 7.7.9 conv2d for example.
To create an MLActivation given builder, name, optional options and optional init-steps, run the following steps:
  1. Let activation be a new MLActivation.

  2. Set activation.[[builder]] to builder.

  3. Set activation.[[name]] to name.

  4. If options is given, set activation.[[options]] to options.

  5. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Make a request to the underlying platform to:

      1. Create platform operator opImpl for the given name operation.

      2. Set activation.[[operator]] to opImpl.

    2. If init-steps are given, run init-steps with options.

      1. Otherwise, initialize activation.[[operator]] given options in an implementation-defined way for the given name operation.

  6. Return activation.

7.4. MLCommandEncoder interface

The MLCommandEncoder interface represents a method of execution that synchronously records the computational workload of a compiled MLGraph to a GPUCommandBuffer on the calling thread. Since the workload is not immediately executed, just recorded, this method allows more flexibility for the caller to determine how and when the recorded commands will be submitted for execution on the GPU relative to other GPU workload on the same or different queue.
typedef (GPUBuffer or GPUTexture) MLGPUResource;

typedef record<DOMString, MLGPUResource> MLNamedGPUResources;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLCommandEncoder {};
MLCommandEncoder has the following internal slots:
[[context]] of type MLContext

The context of type MLContext associated with this MLCommandEncoder.

[[implementation]]

The underlying implementation provided by the User Agent.

7.4.1. Graph Initialization

Record the initialization of the MLGraph. This is a necessary step for optimal performance during graph execution as it gives the platform an opportunity to prepare and optimize constant input data for the subsequent execution of the graph. This method should only be called once per graph.
partial interface MLCommandEncoder {
  undefined initializeGraph(MLGraph graph);
};
Arguments:

Returns: undefined.

The initializeGraph(graph) method steps are:
Graph initialization stage typically involves a process known as "weight preprocessing" where all the constant inputs to the graph are preprocessed and cached at the operating system level for subsequent graph execution calls. The initializing inputs are typically the constant weight data specified through the MLGraphBuilder/constant(value, type) method as constant operands during graph construction time.

7.4.2. Dispatch Execution Commands

Record the MLGraph execution with the inputs MLNamedGPUResources and outputs MLNamedGPUResources.
partial interface MLCommandEncoder {
  undefined dispatch(MLGraph graph, MLNamedGPUResources inputs, MLNamedGPUResources outputs);
};
Arguments:

Returns: undefined.

The dispatch(graph, inputs, outputs) method steps are:
  1. If any of the following requirements are unmet, then throw a "DataError" DOMException.

    1. For each nameinput of inputs:

      1. graph.[[inputDescriptors]][name] must exist.

      2. Let inputDesc be graph.[[inputDescriptors]][name].

      3. If input is a GPUBuffer, then:

        1. input.size must equal to byte length of inputDesc.

    2. For each nameoutput of outputs:

      1. graph.[[outputDescriptors]][name] must exist.

      2. Let outputDesc be graph.[[outputDescriptors]][name].

      3. If output is a GPUBuffer, then:

        1. output.size must equal to byte length of outputDesc.

  2. For each nameinput of inputs:

    1. Set the input of graph.[[implementation]] that is associated with name to input.

  3. For each nameoutput of outputs:

    1. Set the output of graph.[[implementation]] that is associated with name to output.

  4. Issue a compute request of graph.[[implementation]].

  5. If there is an error returned by graph.[[implementation]], then:

    1. Throw an "OperationError" DOMException.

  6. Return undefined.

7.4.3. Generate GPU Command Buffer

Complete the recording of ML workload and return a WebGPU-compatible GPUCommandBuffer containing the recorded workload.
partial interface MLCommandEncoder {
  GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
Arguments:

Returns: GPUCommandBuffer.

The finish(descriptor) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Make a request to the underlying platform to complete the recording of the ML workload, given descriptor.

      See the related WebGPU steps.
  2. Return a GPUCommandBuffer containing the recorded workload.

7.5. MLContext interface

The MLContext interface represents a global state of neural network compute workload and execution processes. Each MLContext object has associated context type, device type and power preference.

The context type is the type of the execution context that manages the resources and facilitates the compilation and execution of the neural network graph:

"default"
Context created per user preference options.
"webgpu"
Context created from WebGPU device.

The device type indicates the kind of device used for the context. It is one of the following:

"cpu"
Provides the broadest compatibility and usability across all client devices with varying degrees of performance.
"gpu"
Provides the broadest range of achievable performance across graphics hardware platforms from consumer devices to professional workstations.

The power preference indicates preference as related to power consumption. It is one of the following:

"default"
Let the user agent select the most suitable behavior.
"high-performance"
Prioritizes execution speed over power consumption.
"low-power"
Prioritizes power consumption over other considerations such as execution speed.
typedef record<DOMString, ArrayBufferView> MLNamedArrayBufferViews;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLContext {};
MLContext has the following internal slots:
[[contextType]] of type context type

The MLContext's context type.

[[deviceType]] of type device type

The MLContext's device type.

[[powerPreference]] of type power preference

The MLContext's power preference.

When the [[contextType]] is set to default with the MLContextOptions.deviceType set to gpu, the user agent is responsible for creating an internal GPU device that operates within the context and is capable of ML workload submission on behalf of the calling application. In this setting however, only ArrayBufferView inputs and outputs are allowed in and out of the graph execution since the application has no way to know what type of internal GPU device is being created on their behalf. In this case, the user agent is responsible for automatic uploads and downloads of the inputs and outputs to and from the GPU memory using this said internal device.

7.5.1. Synchronous Execution

Synchronously carries out the computational workload of a compiled graph MLGraph on the calling thread, which must be a worker thread, to produce results as defined by the operations in the graph. This method of execution requires an MLContext created with MLContextOptions. Otherwise, it throws an "OperationError" DOMException.
partial interface MLContext {
  [Exposed=(DedicatedWorker)]
  undefined computeSync(
      MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs);
};
Arguments:

Returns: undefined.

The computeSync(graph, inputs, outputs) method steps are:
  1. If graph.[[context]].[[contextType]] is not "default", throw an "OperationError" DOMException.

  2. If validating graph resources given inputs and graph.[[inputDescriptors]] returns false, then throw a "DataError" DOMException.

  3. If validating graph resources given outputs and graph.[[outputDescriptors]] returns false, then throw a "DataError" DOMException.

  4. Invoke execute graph given graph, inputs and outputs.

  5. If that throws an error, re-throw the error.

  6. Return undefined.

To validate graph resources, given resources and descriptors, run the following steps:
  1. For each nameresource of resources:

    1. If descriptors[name] does not exist, return false.

    2. If validating buffer with descriptor given resource and descriptors[name] returns false, then return false.

  2. Return true.

To validate buffer with descriptor given bufferView and descriptor, run the following steps:
  1. If bufferView’s element type does not match to descriptor.dataType according to this table, return false.

  2. If bufferView.[[ByteLength]] is not equal to the byte length of descriptor, return false.

To execute graph, given graph, inputs and outputs, run the following steps:
  1. Let inputResources denote the input resources of graph.[[implementation]].

  2. For each nameinputValue of inputs:

    1. Let inputDescriptor be graph.[[inputDescriptors]][name].

    2. Let inputTensor be a new tensor for graph.[[implementation]] as follows:

      1. Set the data type of inputTensor to the one that matches the element type of inputValue.

      2. Set the dimensions of inputTensor to inputDescriptor.dimensions.

      3. Set the values of elements in inputTensor to the values of elements in inputValue.

    3. Request the underlying implementation of graph to bind inputResources[name] to inputTensor.

  3. For each nameoutputValue of outputs:

    1. Issue a compute request to graph.[[implementation]] given name and inputResources and wait for completion.

      1. If that returns an error, then throw an "OperationError" DOMException.

      2. Otherwise, store the result in outputTensor.

    2. Let outputDesc be graph.[[outputDescriptors]][name].

    3. If the byte length of outputTensor is not equal to the byte length of outputDesc, then throw a "DataError" DOMException.

    4. If the element type of outputTensor doesn’t match the element type of outputValue, then throw a "DataError" DOMException.

    5. Request the underlying implementation of graph to set the values of elements in outputValue to the values of elements in outputTensor.

  4. Return undefined.

7.5.1.1. Examples
The following code showcases the synchronous computation with optional outputs in a worker.
const context = navigator.ml.createContextSync();

// Build a graph with two outputs.
const builder = new MLGraphBuilder(context);
const descA = {dataType: 'float32', dimensions: [3, 4]};
const a = builder.input('a', descA);
const descB = {dataType: 'float32', dimensions: [4, 3]};
const bufferB = new Float32Array(sizeOfShape(descB.dimensions)).fill(0.5);
const b = builder.constant(descB, bufferB);
const descC = {dataType: 'float32', dimensions: [3, 3]};
const bufferC = new Float32Array(sizeOfShape(descC.dimensions)).fill(1);
const c = builder.constant(descC, bufferC);
const d = builder.matmul(a, b);
const e = builder.add(d, c);
const graph = builder.buildSync({'d': d, 'e': e});

const bufferA = new Float32Array(sizeOfShape(descA.dimensions)).fill(0.5);
const inputs = {'a': bufferA};

// Compute d.
const bufferD = new Float32Array(sizeOfShape([3, 3]));
context.computeSync(graph, inputs, {'d': bufferD});
console.log(`values: ${bufferD}`);

// Compute e.
const bufferE = new Float32Array(sizeOfShape([3, 3]));
context.computeSync(graph, inputs, {'e': bufferE});
console.log(`values: ${bufferE}`);

7.5.2. MLNamedArrayBufferViews transfer algorithm

To transfer an MLNamedArrayBufferViews views:
  1. Let transferredViews be a new MLNamedArrayBufferViews.

  2. For each nameview of views:

    1. Let transferredBuffer be the result of transferring the underlying buffer of view.

    2. Let constructor be the appropriate view constructor for the type of ArrayBufferView view.

    3. Let elementsNumber be the result of the byte length of view ÷ element size of view.

    4. Let transferredView be Construct(constructor, transferredBuffer, view.[[ByteOffset]], elementsNumber).

    5. Set transferredViews[name] to transferredView.

  3. Return transferredViews.

7.5.3. Asynchronous Execution

Asynchronously carries out the computational workload of a compiled graph MLGraph on a separate timeline, either on a worker thread for the CPU execution, or on a GPU timeline for the submission of GPU workload on the command queue. The asynchronous nature of this call avoids blocking the calling thread while the computation for result is ongoing. This method of execution requires an MLContext created with MLContextOptions. Otherwise, it throws an "OperationError" DOMException.
In accordance with the Web IDL warning, to prevent the calling thread from modifying the input and output resources while the computation is ongoing, this method transfers the input and output MLNamedArrayBufferViews to new views that share the same backing memory allocations. The transferred views are returned to the caller via the promise fulfillment with the computation result written into the backing memory of the output views.
dictionary MLComputeResult {
  MLNamedArrayBufferViews inputs;
  MLNamedArrayBufferViews outputs;
};

partial interface MLContext {
  Promise<MLComputeResult> compute(
      MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs);
};
Arguments:

Returns: Promise<MLComputeResult>.

The compute(graph, inputs, outputs) method steps are:
  1. Let promise be a new promise.

  2. Return promise and run the following steps in parallel:

    1. If graph.[[context]].[[contextType]] is not "default", reject promise with an "OperationError" DOMException.

    2. If validating graph resources given inputs and graph.[[inputDescriptors]] returns false, then reject promise with a "DataError" DOMException.

    3. If validating graph resources given outputs and graph.[[outputDescriptors]] returns false, then reject promise with a "DataError" DOMException.

    4. Let transferredInputs be the result of transferring MLNamedArrayBufferViews inputs.

    5. Let transferredOutputs be the result of transferring MLNamedArrayBufferViews outputs.

    6. Invoke execute graph given graph, transferredInputs and transferredOutputs.

    7. If that throws an error, reject promise with the error.

    8. Otherwise, when execute graph has completed:

      1. Let result be a new MLComputeResult.

      2. Set result.inputs to transferredInputs.

      3. Set result.outputs to transferredOutputs.

      4. Resolve promise with result.

7.5.3.1. Examples
The following code showcases the asynchronous computation.
const operandType = {dataType: 'float32', dimensions: [2, 2]};
const context = await navigator.ml.createContext();
const builder = new MLGraphBuilder(context);
// 1. Create a computational graph 'C = 0.2 * A + B'.
const constant = builder.constant(0.2);
const A = builder.input('A', operandType);
const B = builder.input('B', operandType);
const C = builder.add(builder.mul(A, constant), B);
// 2. Compile it into an executable.
const graph = await builder.build({'C': C});
// 3. Bind inputs to the graph and execute for the result.
const bufferA = new Float32Array(4).fill(1.0);
const bufferB = new Float32Array(4).fill(0.8);
const bufferC = new Float32Array(4);
const inputs = {'A': bufferA, 'B': bufferB};
const outputs = {'C': bufferC};
const result = await context.compute(graph, inputs, outputs);
// The computed result of [[1, 1], [1, 1]] is in the buffer associated with
// the output operand.
console.log('Output value: ' + result.outputs.C);
// Note: the result.outputs.C buffer is different from the bufferC, but it
// shares the same backing memory allocation.

7.5.4. WebGPU Interoperability

Create MLCommandEncoder interface used to record the ML workload onto a WebGPU-compatible GPUCommandBuffer to allow mixing of ML workload with other GPU workload in an application that leverages WebGPU. This method only succeeds on an MLContext created with GPUDevice. Otherwise, it throws an "OperationError" DOMException.
partial interface MLContext {
  MLCommandEncoder createCommandEncoder();
};
Returns: MLCommandEncoder. The command encoder used to record ML workload on the GPU.

7.6. MLGraph interface

The MLGraph interface represents a compiled computational graph. A compiled graph once constructed is immutable and cannot be subsequently changed.
[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraph {};
MLGraph has the following internal slots:
[[context]] of type MLContext

The context of type MLContext associated with this MLGraph.

[[inputDescriptors]] of type record<DOMString, MLOperandDescriptor>

Maps the name of an input MLOperand to its MLOperandDescriptor for all input MLOperands of this MLGraph.

[[outputDescriptors]] of type record<DOMString, MLOperandDescriptor>

Maps the name of an output MLOperand to its MLOperandDescriptor for all output MLOperands of this MLGraph.

[[implementation]]

The underlying implementation provided by the User Agent.

7.7. MLGraphBuilder interface

The MLGraphBuilder interface defines a set of operations as identified by the § 2 Use cases that can be composed into a computational graph. It also represents the intermediate state of a graph building session.

typedef record<DOMString, MLOperand> MLNamedOperands;

dictionary MLBufferResourceView {
  required GPUBuffer resource;
  unsigned long long offset = 0;
  unsigned long long size;
};

typedef (ArrayBufferView or MLBufferResourceView) MLBufferView;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraphBuilder {
  // Construct the graph builder from the context.
  constructor(MLContext context);

  // Create an operand for a graph input.
  MLOperand input(DOMString name, MLOperandDescriptor descriptor);

  // Create an operand for a graph constant.
  MLOperand constant(MLOperandDescriptor descriptor, MLBufferView bufferView);

  // Create a single-value operand from the specified number of the specified type.
  MLOperand constant(double value, optional MLOperandDataType type = "float32");

  // Compile the graph up to the specified output operands asynchronously.
  Promise<MLGraph> build(MLNamedOperands outputs);

  // Compile the graph up to the specified output operands synchronously.
  [Exposed=(DedicatedWorker)]
  MLGraph buildSync(MLNamedOperands outputs);
};
Both MLGraphBuilder.build() and MLGraphBuilder.buildSync() methods compile the graph builder state up to the specified output operands into a compiled graph according to the type of MLContext that creates it. Since this operation can be costly in some machine configurations, the calling thread of the MLGraphBuilder.buildSync() method must only be a worker thread to avoid potential disruption of the user experience. When the [[contextType]] of the MLContext is set to "default", the compiled graph is initialized right before the MLGraph is returned. This graph initialization stage is important for optimal performance of the subsequent graph executions. See § 7.4.1 Graph Initialization for more detail.

MLBufferResourceView has the following members:

resource, of type GPUBuffer

A GPUBuffer object. Specifies the GPU buffer source.

offset, of type unsigned long long, defaulting to 0

Specifies an unsigned long long offset in the buffer source.

size, of type unsigned long long

Specifies the unsigned long long size of the buffer view.

MLGraphBuilder has the following internal slots:
[[context]] of type MLContext

The context of type MLContext associated with this MLGraphBuilder.

7.7.1. MLGraphBuilder constructor

The new MLGraphBuilder(context) constructor steps are:

7.7.2. argMin/Max

Return the index location of the minimum or maxmium values of all the input values along the axes.
dictionary MLArgMinMaxOptions {
  sequence<unsigned long> axes = null;
  boolean keepDimensions = false;
  boolean selectLastIndex = false;
};

partial interface MLGraphBuilder {
  MLOperand argMin(MLOperand input, optional MLArgMinMaxOptions options = {});
  MLOperand argMax(MLOperand input, optional MLArgMinMaxOptions options = {});
};

MLArgMinMaxOptions has the following members:

axes, of type sequence<unsigned long>, defaulting to null

A sequence of unsigned long. The dimensions to reduce. The values in the sequence must be in the range [0, N-1] where N is the rank of the input tensor. If not present, all dimensions are reduced.

keepDimensions, of type boolean, defaulting to false

A boolean. If true, retains reduced dimensions with size 1. The default value is false.

selectLastIndex, of type boolean, defaulting to false

A boolean. If true, select the last index instead of the first found along the axes. The default value is false.

Arguments:

Returns: an MLOperand. The N-D tensor of the reduced shape. The values must be of type "int64" in the range [0, N-1] where N is the corresponding size of each of the input dimensions specified by options.axes.

To create argMin/argMax operation given op, MLOperand input and MLArgMinMaxOptions options, run the following steps:
  1. Assert: op is one of "argMin", "argMax".

  2. If options.axes exists, if any of its elements is not in the range 0 to the rank of input, exclusive, then throw a "DataError" DOMException.

  3. Let outputShape be the result of invoking the underlying implementation for calculating reduction output dimensions, given options.

  4. Let desc be a new MLOperandDescriptor.

  5. Set desc.dataType to "int64".

  6. Set desc.dimensions to outputShape.

  7. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the op argMin or argMax operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  8. Return output.

The following argMin/argMax algorithms are supported.
The argMin(input, options) method steps are:
  1. Let output be the result of running the create argMin/argMax operation given "argMin", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The argMax(input, options) method steps are:
  1. Let output be the result of running the create argMin/argMax operation given "argMax", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.3. batchNormalization

Normalize the values of the input tensor using [Batch-Normalization]. For each input feature, the mean and variance values of that feature are computed across all the samples in the batch dimension while the model is trained. These mean and variance values are then subsequently given to this operation during model inference.
dictionary MLBatchNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  unsigned long axis = 1;
  float epsilon = 1e-5;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance,
                             optional MLBatchNormalizationOptions options = {});
};

MLBatchNormalizationOptions has the following members:

scale, of type MLOperand

An MLOperand. Specifies the 1-D tensor of the scaling values whose is equal to the size of the input dimension denoted by axis.

bias, of type MLOperand

An MLOperand. Specifies the 1-D tensor of the bias values whose size is equal to the size of the input dimension denoted by axis.

axis, of type unsigned long, defaulting to 1

An unsigned long scalar. Specifies the index to the feature count dimension of the input shape for which the mean and variance values are. Its value must be in the range [0, N-1] where N is the rank of the input tensor. The default value is 1, corresponding to the channel ("c") dimension in the "nchw" data layout.

epsilon, of type float, defaulting to 1e-5

A float scalar. Specifies A small value to prevent computational error due to divide-by-zero.

activation, of type MLActivation

An MLActivation object. Specifies the optional activation function that immediately follows the normalization operation.

Arguments:

Returns: an MLOperand. The batch-normalized N-D tensor of the same shape as input.

The batchNormalization(input, mean, variance, options) method steps are:
  1. If options.axis is not in the range 0 to the rank of input, exclusive, then throw a TypeError.

  2. If the size of mean.[[descriptor]].dimensions is not 1, then throw a TypeError.

  3. If mean.[[descriptor]].dimensions[0] is not equal to input.[[descriptor]].dimensions[options.axis], then throw a TypeError.

  4. If the size of variance.[[descriptor]].dimensions is not 1, then throw a TypeError.

  5. If variance.[[descriptor]].dimensions[0] is not equal to input.[[descriptor]].dimensions[options.axis], then throw a TypeError.

  6. If options.scale exists:

    1. If its size is not 1, then throw a TypeError.

    2. If options.scale.[[descriptor]].dimensions[0] is not equal to input.[[descriptor]].dimensions[options.axis], then throw a TypeError.

  7. If options.bias exists:

    1. If its size is not 1, then throw a TypeError.

    2. If options.bias.[[descriptor]].dimensions[0] is not equal to input.[[descriptor]].dimensions[options.axis], then throw a TypeError.

  8. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and input.[[descriptor]], that may use the same underlying data as input.

    2. Make a request to the underlying platform to initialize the batch normalization:

      1. Create platform operator batchNormImpl for this method, given input, mean, variance and options.

      2. If options.activation exists,register it as activation to batchNormImpl.

      3. Connect output as output to batchNormImpl.

  9. Return output.

The behavior of this operation when the input tensor is 4-D of the "nchw" layout and the activation is of operator type relu can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
const shape = [1,null,1,1];
return builder.relu(
  builder.add(
    builder.mul(
      builder.reshape(options.scale, shape),
      builder.div(
        builder.sub(input, builder.reshape(mean, shape)),
        builder.sqrt(builder.add(builder.reshape(variance, shape), builder.constant(options.epsilon)))
        )),
    builder.reshape(options.bias, shape)));

7.7.4. build

Build a composed graph up to a given output operand into a computational graph, asynchronously or synchronously.
7.7.4.1. build(outputs)
The build(outputs) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. Let promise be a new promise.

  2. Return promise and run the following steps in parallel.

  3. Return the result of invoking buildSync(outputs) given outputs.

    1. If that throws, re-throw the error.

7.7.4.2. buildSync(outputs)
The buildSync(outputs) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. If outputs is empty, then throw a TypeError.

  2. For each nameoperand of outputs:

    1. If name is empty, then throw a TypeError.

  3. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let graph be a new MLGraph:

      1. Set graph.[[context]] to this.[[context]].

    2. Make a request to the underlying platform to:

      1. Connect graph to a new implementation-defined graph implementation graphImpl given graph.

      2. Set graph.[[implementation]] to graphImpl.

    3. Make a request to the underlying platform to initialize the graph:

      1. For each nameoperand of outputs:

        1. If validating MLOperand given operand and this returns false, then throw a TypeError.

        2. If operand was created as an input by the underlying platform:

          1. If operand.[[name]]] is not unique for graphImpl, then throw a TypeError.

          2. Add operand.[[descriptor]] to graph.[[inputDescriptors]][operand.[[name]]].

        3. If operand was created as a constant by the underlying platform:

          1. Implementations MAY preprocess and optimize the tensor data of operand for the underlying platform.

        4. Register operand.[[operand]] in graphImpl as graph output.

        5. Register operand.[[operator]] to graphImpl.

  4. Return graph.

7.7.5. cast

Cast each element in the input tensor to the target data type.
partial interface MLGraphBuilder {
  MLOperand cast(MLOperand input, MLOperandDataType type);
};
Arguments:

Returns: an MLOperand. The N-D tensor of the same shape as input with each element casted to the target data type.

The cast(input, type) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let operand be the result of creating an MLOperand given this, input and type.

    2. Let output be the result of copying an MLOperand given input.

    3. Make a request to the underlying platform to:

      1. Create platform operator castImpl for this method, given type.

      2. Set output.[[operator]] to castImpl.

      3. Create an platform operand outputImpl to represent an output, given output and castImpl.

      4. Set output.[[operand]] to outputImpl.

    4. Connect operand.[[operand]] as input to castImpl.

    5. Connect output.[[operand]] as output to castImpl.

  2. Return output.

7.7.6. clamp

Clamp the input tensor element-wise within a range specified by the minimum and maximum values.
dictionary MLClampOptions {
  float minValue;
  float maxValue;
};

partial interface MLGraphBuilder {
  MLOperand clamp(MLOperand input, optional MLClampOptions options = {});
  MLActivation clamp(optional MLClampOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
if (options.minValue === undefined) {
  if (options.maxValue === undefined) {
    return input;
  } else {
    return builder.min(input, builder.constant(options.maxValue));
  }
} else {
  if (options.maxValue === undefined) {
    return builder.max(input, builder.constant(options.minValue));
  } else {
    return builder.min(
        builder.max(input, builder.constant(options.minValue)),
        builder.constant(options.maxValue));
  }
}
To check clamp options given options, run the following steps:
  1. If options.minValue is greater than options.maxValue, then return false.

  2. Return true.

7.7.6.1. clamp(input, options)
Arguments: Returns:
The clamp(input, options) method steps are:
  1. If checking clamp options given options returns false, then throw a TypeError.

  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Create platform operator clampImpl for this method, given options.minValue and options.maxValue.

      2. Set output.[[operator]] to clampImpl.

      3. Create an platform operand outputImpl to represent clamp output, given output and clampImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to clampImpl.

    4. Connect output.[[operand]] as output to clampImpl.

  3. Return output.

7.7.6.2. clamp(options)
Arguments: Returns:
The clamp(options) method steps are:
  1. If checking clamp options given options returns false, then throw a TypeError.

  2. Let op be the result of creating an MLActivation given this, "clamp" and options.

    1. If that throws an error, re-throw the error.

  3. Return op.

7.7.7. concat

Concatenates the input tensors along a given axis.
partial interface MLGraphBuilder {
  MLOperand concat(sequence<MLOperand> inputs, unsigned long axis);
};
Arguments:

Returns: an MLOperand. The concatenated tensor of all the inputs along the axis. The output tensor has the same shape except on the dimension that all the inputs concatenated along. The size of that dimension is computed as the sum of all the input sizes of the same dimension.

The concat(inputs, axis) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. Assert: the shape, i.e. dimensions of each operand in inputs is the same, except on the dimension given by axis on which they are concatenated.

  2. Assert: the dataType of each operand in inputs is the same.

  3. If any of the following steps fail, then throw a "DataError" DOMException.

    1. Let desc be inputs[0].[[descriptor]].

    2. If axis is greater than or equal to the rank of desc, then throw a "DataError" DOMException.

    3. Set desc.dimensions[axis] to 0.

    4. For each index in the range 0 to the rank of inputs, exclusive:

      1. Let input be inputs[index].

      2. If validating MLOperand given input and this returns false, then throw a "DataError" DOMException.

      3. If input.[[descriptor]].dataType is not equal to inputs[0].[[descriptor]].dataType, then throw a "DataError" DOMException.

      4. For each dim in the range 0 to the rank of input.[[descriptor]].dimensions, exclusive:

        If the shape of each corresponding dimension and type of the operands, except for those of the dimension given by axis, is not the same, fail.
        1. If dim is not equal to axis and if input.dimensions[dim] is not equal to inputs[0].dimensions[dim], then throw a "DataError" DOMException.

        2. If dim is equal to axis, add to desc.dimensions[axis] the value of input.dimensions[dim].

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Create platform operator concatImpl for this method, given inputs and axis.

      2. Set output.[[operator]] to concatImpl.

      3. Create an platform operand outputImpl to represent output,given output and concatImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect inputs as input to concatImpl.

    4. Connect output.[[operand]] as output to concatImpl.

  5. Return output.

7.7.8. constant

Create a constant MLOperand that can be used in MLGraphBuilder methods.
7.7.8.1. constant(descriptor, bufferView)
Create a constant MLOperand of the specified data type and shape that contains the initializing data.
Arguments: Returns:: an MLOperand. The constant output tensor.
The constant(descriptor, bufferView) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. If the byte length of descriptor is not supported by the underlying platform, then throw a "DataError" DOMException.

  2. If the checking dimensions given descriptor.dataType and descriptor.dimensions returns false, then throw a "DataError" DOMException.

  3. If validating buffer with descriptor given bufferView and descriptor returns false, then throw a TypeError.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let operand be the result of creating an MLOperand given this and descriptor.

    2. Let bytes be the result of getting a copy of the bytes held by the buffer source given bufferView.

    3. Make a request to the underlying platform to:

      1. Create an platform operand constantImpl to represent a constant, given descriptor.

      2. Set operand.[[operand]] to constantImpl.

      3. Register operand as a tensor constant with bytes as value.

  5. Return operand.

7.7.8.2. constant(value, type)
Create a constant MLOperand of the specified value and data type.
Data truncation will occur when the specified value exceeds the range of the specified output data type e.g. when a float value is assigned to an "int8" data type, etc.
Arguments: Returns:: an MLOperand. The constant output.
The constant(value, type) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. Let descriptor be a new MLOperandDescriptor.

    1. Set descriptor.dataType to type.

    2. Set descriptor.dimensions to undefined.

      In the case of a scalar constant, descriptor.dimensions is ignored.
  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let operand be the result of creating an MLOperand given this and descriptor.

    2. Make a request to the underlying platform to:

      1. Create an platform operand constantImpl to represent a constant, given descriptor.

      2. Set operand.[[operand]] to constantImpl.

      3. Register operand as a scalar constant with value as value.

  3. Return operand.

7.7.8.3. constant(start, end, step, type)
Create a constant MLOperand of the specified data type and shape that contains the data as specified by the range.
Data truncation will occur when the values in the range exceed the range of the specified output data type e.g. when a float value is assigned to an "int8" data type, etc.
Arguments: Returns:: an MLOperand. The constant 1-D output tensor of size max(0, ceil((end - start)/step)).
The constant(start, end, step, type) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. Let descriptor be a new MLOperandDescriptor.

    1. Set descriptor.dataType to type.

    2. Let size be max(0, ceil((end - start)/step)).

    3. Set descriptor.dimensions to the list « size ».

  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let operand be the result of creating an MLOperand given this, start, end, step, and type.

    2. Make a request to the underlying platform to:

      1. Create an implementation-defined platform memory buffer the size of size multiplied by sizeof(descriptor.dataType).

      2. Store the beginning address to that memory buffer as a pointer buffer of the corresponding data type.

    3. For each index in the range 0 to size, exclusive:

      1. Set buffer[index] to start + (index * step).

    4. Make a request to the underlying platform to:

      1. Create an platform operand constantImpl to represent a constant operand, given descriptor.

      2. Set operand.[[operand]] to constantImpl.

      3. Register operand as a constant with buffer as value.

  3. Return operand.

7.7.9. conv2d

Compute a 2-D convolution given 4-D input and filter tensors
enum MLConv2dFilterOperandLayout {
  "oihw",
  "hwio",
  "ohwi",
  "ihwo"
};

enum MLAutoPad {
  "explicit",
  "same-upper",
  "same-lower"
};

dictionary MLConv2dOptions {
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  MLAutoPad autoPad = "explicit";
  unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConv2dFilterOperandLayout filterLayout = "oihw";
  MLOperand bias;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand conv2d(MLOperand input, MLOperand filter, optional MLConv2dOptions options = {});
};

MLConv2dOptions has the following members:

padding, of type sequence<unsigned long>

A sequence of unsigned long of length 4: [beginningHeight, endingHeight, beginningWidth, endingWidth]. Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input. The default value is [0, 0, 0, 0].

strides, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [strideHeight, strideWidth]. Specifies the stride of the sliding window for each spatial dimension of the convolution input. The default value is [1, 1].

dilations, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [dilationHeight, dilationWidth]. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel). The default value is [1, 1].

autoPad, of type MLAutoPad, defaulting to "explicit"

An MLAutoPad string. Specifies the automatic input padding options. The default value is "explicit", which means that the values in the padding array should be used for input padding. When the option is set other than "explicit", the values in the padding array are ignored.

With the "same-upper" option, the padding values are automatically computed such that the additional ending padding of the spatial input dimensions would allow all of the input values in the corresponding dimension to be filtered.

The "same-lower" option is similar but padding is applied to the beginning padding of the spatial input dimensions instead of the ending one.

groups, of type unsigned long, defaulting to 1

An unsigned long scalar. Specifies the number of groups that input channels and output channels are divided into. The default value is 1.

inputLayout, of type MLInputOperandLayout, defaulting to "nchw"

An MLInputOperandLayout string. Specifies the layout format of the input and output tensor as follows:

  • "nchw"

    • input tensor: [batches, inputChannels, height, width]

    • output tensor: [batches, outputChannels, height, width]

  • "nhwc":

    • input tensor: [batches, height, width, inputChannels]

    • output tensor: [batches, height, width, outputChannels]

The default value is "nchw".
filterLayout, of type MLConv2dFilterOperandLayout, defaulting to "oihw"

An MLConv2dFilterOperandLayout string. Specifies the layout format of the filter tensor as follow:

  • "oihw": [outputChannels, inputChannels/groups, height, width]

  • "hwio": [height, width, inputChannels/groups, outputChannels]

  • "ohwi": [outputChannels, height, width, inputChannels/groups]

  • "ihwo": [inputChannels/groups, height, width, outputChannels]

The default value is "oihw".
bias, of type MLOperand

An MLOperand object. Specifies the additional 1-D tensor with the shape of [outputChannels] whose values are to be added to the convolution result.

activation, of type MLActivation

An MLActivation object. Specifies the optional activation function that immediately follows the convolution operation.

Arguments:

Returns: an MLOperand. The output 4-D tensor that contains the convolution result. The output shape is interpreted according to the options.inputLayout value. More specifically, the spatial dimensions or the sizes of the last two dimensions of the output tensor for the nchw input layout can be calculated as follow:

outputSize = 1 + (inputSize - (filterSize - 1) * dilation - 1 + beginningPadding + endingPadding) / stride

A depthwise conv2d operation is a variant of grouped convolution, used in models like the MobileNet, where the options.groups = inputChannels = outputChannels and the shape of filter tensor is [options.groups, 1, height, width] for "oihw" layout, [height, width, 1, options.groups] for "hwio" layout, [options.groups, height, width, 1] for "ohwi" layout and [1, height, width, options.groups] for "ihwo" layout.
The conv2d(input, filter, options) method steps are:
  1. Let inputSize be the size of input.[[descriptor]].dimensions.

  2. Let filterSize be the size of filter.[[descriptor]].dimensions.

  3. If inputSize is not 4, then throw a "DataError" DOMException.

  4. If filterSize is not 4, then throw a "DataError" DOMException.

  5. If input.[[descriptor]].dataType is not the same as filter.[[descriptor]].dataType, then throw a TypeError.

  6. If options.padding does not exist, set it to the list « 0, 0, 0, 0 ».

  7. Else if the size of options.padding is not 4, then throw a "DataError" DOMException.

  8. If options.strides does not exist, set it to the list « 1, 1 ».

  9. Else if the size of options.strides is not 2, then throw a TypeError.

  10. If any element in options.strides is equal to 0, then throw a TypeError.

  11. If options.dilations does not exist, set it to the list « 1, 1 ».

  12. Else if the size of options.dilations is not 2, then throw a TypeError.

  13. If options.autoPad does not exist, set it to "explicit".

  14. If options.groups is 0, then throw a "DataError" DOMException.

  15. If inputSize / options.groups is not equal to filterSize, then throw a "DataError" DOMException.

  16. Else if inputSize % options.groups is not 0, then throw a "DataError" DOMException.

  17. If options.bias exists:

    1. If the size of options.bias.[[descriptor]].dimensions is not 1, then throw a TypeError.

    2. If options.bias.[[descriptor]].dataType is not the same as input.[[descriptor]].dataType, then throw a TypeError.

  18. Let outputShape be the result of invoking the underlying implementation for calculating output dimensions, given options.

  19. If outputShape is not the same as the shape of options.bias.[[descriptor]].dimensions, then throw a "DataError" DOMException.

  20. Let desc be a new MLOperandDescriptor.

  21. Set desc.dataType to input.[[descriptor]].dataType.

  22. Set desc.dimensions to outputShape.

  23. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Create platform operator conv2dImpl for this method, given options and filter.

        1. If options.activation exists,register it as activation to conv2dImpl.

      2. Set output.[[operator]] to conv2dImpl.

      3. Create an platform operand outputImpl to represent the output, given output and conv2dImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to conv2dImpl.

    4. Connect output.[[operand]] as output to conv2dImpl.

  24. Return output.

7.7.10. convTranspose2d

Compute a 2-D transposed convolution given 4-D input and filter tensors
enum MLConvTranspose2dFilterOperandLayout {
  "iohw",
  "hwoi",
  "ohwi"
};

dictionary MLConvTranspose2dOptions {
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  sequence<unsigned long> outputPadding;
  sequence<unsigned long> outputSizes;
  MLAutoPad autoPad = "explicit";
  unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConvTranspose2dFilterOperandLayout filterLayout = "iohw";
  MLOperand bias;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand convTranspose2d(MLOperand input, MLOperand filter,
                            optional MLConvTranspose2dOptions options = {});
};

MLConvTranspose2dOptions has the following members:

padding, of type sequence<unsigned long>

A sequence of unsigned long of length 4: [beginningHeight, endingHeight, beginningWidth, endingWidth]. Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input. The default value is [0, 0, 0, 0].

strides, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [strideHeight, strideWidth]. Specifies the stride of the sliding window for each spatial dimension of the convolution input. The default value is [1, 1].

dilations, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [dilationHeight, dilationWidth]. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel). The default value is [1, 1].

outputPadding, of type sequence<unsigned long>

A sequence of unsigned long of length 2. Specifies the padding values applied to each spatial dimension of the output tensor. The explicit padding values are needed to disambiguate the output tensor shape for transposed convolution when the value of the options.strides is greater than 1.

Note that these values are only used to disambiguate output shape when needed; it does not necessarily cause any padding value to be written to the output tensor.

The default values is [0, 0].

outputSizes, of type sequence<unsigned long>

A sequence of unsigned long of length 2. Specifies the sizes of the last two dimensions of the output tensor. When the output sizes are explicitly specified, the output padding values in outputPadding are ignored.

If not specified, the output sizes are automatically computed.

autoPad, of type MLAutoPad, defaulting to "explicit"

An MLAutoPad string. Specifies the automatic input padding options. The default value is "explicit", which means that the values in the padding array should be used for input padding.

When the option is set other than "explicit", the values in the padding array are ignored.

With the "same-upper" option, the padding values are automatically computed such that the additional ending padding of the spatial input dimensions would allow all of the input values in the corresponding dimension to be filtered.

The "same-lower" option is similar but padding is applied to the beginning padding of the spatial input dimensions instead of the ending one.

groups, of type unsigned long, defaulting to 1

An unsigned long scalar. Specifies the number of groups that input channels and output channels are divided into. The default value is 1.

inputLayout, of type MLInputOperandLayout, defaulting to "nchw"

An MLInputOperandLayout string. Specifies the layout format of the input and output tensor as follows:

  • "nchw"

    • input tensor: [batches, inputChannels, height, width]

    • output tensor: [batches, outputChannels, height, width]

  • "nhwc":

    • input tensor: [batches, height, width, inputChannels]

    • output tensor: [batches, height, width, outputChannels]

The default value is "nchw".
filterLayout, of type MLConvTranspose2dFilterOperandLayout, defaulting to "iohw"

An MLConvTranspose2dFilterOperandLayout string. Specifies the layout format of the filter tensor as follow:

  • "iohw": [inputChannels, outputChannels/groups, height, width]

  • "hwoi": [height, width, outputChannels/groups, inputChannels]

  • "ohwi": [outputChannels/groups, height, width, inputChannels]

The default value is "iohw".
bias, of type MLOperand

An MLOperand object. Specifies the additional 1-D tensor with the shape of [outputChannels] whose values are to be added to the convolution result.

activation, of type MLActivation

An MLActivation object. Specifies the optional activation function that immediately follows the convolution operation.

Arguments:

Returns: an MLOperand. The output 4-D tensor that contains the transposed convolution result. The output shape is interpreted according to the options.inputLayout value. More specifically, unless the options.outputSizes values are explicitly specified, the options.outputPadding may be needed to compute the spatial dimension values of the output tensor as follow:

outputSize = (inputSize - 1) * stride + (filterSize - 1) * dilation + 1 - beginningPadding - endingPadding + outputPadding

The convTranspose2d(input, filter, options) method steps are:
  1. Let inputSize be the size of input.[[descriptor]].dimensions.

  2. Let filterSize be the size of filter.[[descriptor]].dimensions.

  3. If inputSize is not 4, then throw a "DataError" DOMException.

  4. If filterSize is not 4, then throw a "DataError" DOMException.

  5. If input.[[descriptor]].dataType is not the same as [[descriptor]].dataType, then throw a TypeError.

  6. If options.padding does not exist, set it to the list « 0, 0, 0, 0 ».

  7. Else if the size of options.padding is not 4, then throw a "DataError" DOMException.

  8. If options.strides does not exist, set it to the list « 1, 1 ».

  9. Else if the size of options.strides is not 2, then throw a TypeError.

  10. If any element in options.strides is equal to 0, then throw a TypeError.

  11. If options.dilations does not exist, set it to the list « 1, 1 ».

  12. Else if the size of options.dilations is not 2, then throw a TypeError.

  13. If options.outputPadding does not exist, set it to the list « 0, 0 ».

  14. Else if the size of options.outputPadding is not 2, then throw a TypeError.

  15. If options.outputSizes exists:

    1. If the size of options.outputSizes is not 2, then throw a TypeError.

    2. If the elements of options.outputSizes are not smaller than the elements at the same dimension (index) for options.strides, then throw a "DataError" DOMException.

  16. If inputSize / options.groups is not equal to filterSize, then throw a "DataError" DOMException.

  17. Else if inputSize % options.groups is not 0, then throw a "DataError" DOMException.

  18. If options.bias exists:

    1. If the size of options.bias.[[descriptor]].dimensions is not 1, then throw a TypeError.

    2. If options.bias.[[descriptor]].dataType is not the same as input.[[descriptor]].dataType, then throw a TypeError.

  19. Let outputShape be the result of invoking the underlying implementation for calculating output dimensions, given options.

  20. If outputShape is not the same as the shape of options.bias.[[descriptor]].dimensions, then throw a "DataError" DOMException.

  21. Let desc be a new MLOperandDescriptor.

  22. Set desc.dataType to input.[[descriptor]].dataType.

  23. Set desc.dimensions to outputShape.

  24. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Create platform operator convTranspose2dImpl for this method, given options and filter.

        1. If options.activation exists,register it as activation to convTranspose2dImpl.

      2. Set output.[[operator]] to convTranspose2dImpl.

      3. Create an platform operand outputImpl to represent the output, given output and convTranspose2dImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to convTranspose2dImpl.

    4. Connect output.[[operand]] as output to convTranspose2dImpl.

  25. Return output.

7.7.11. Element-wise binary operations

Compute the element-wise binary addition, subtraction, multiplication, division, power, maximum and minimum of the two input tensors.

The element-wise binary operations will be broadcasted according to [numpy-broadcasting-rule]. The rank of the output tensor is the maximum rank of the input tensors. For each dimension of the output tensor, its size is the maximum size along that dimension of the input tensors.

partial interface MLGraphBuilder {
  MLOperand add(MLOperand a, MLOperand b);
  MLOperand sub(MLOperand a, MLOperand b);
  MLOperand mul(MLOperand a, MLOperand b);
  MLOperand div(MLOperand a, MLOperand b);
  MLOperand max(MLOperand a, MLOperand b);
  MLOperand min(MLOperand a, MLOperand b);
  MLOperand pow(MLOperand a, MLOperand b);
};
Arguments:

Returns: an MLOperand. The output tensor that contains the result of element-wise binary operation of the two input tensors.

Operation types:
To create element-wise binary operation given op, MLOperand a and MLOperand b, run the following steps:
  1. Assert: op is one of "add", "sub", "mul", "div", "max", "min", "pow".

  2. If a.[[descriptor]].dataType is not equal to b.[[descriptor]].dataType, then throw a "DataError" DOMException.

  3. Let descriptor be a new MLOperandDescriptor.

  4. Set descriptor.dataType to a.[[descriptor]].dataType.

  5. Set descriptor.dimensions to the result of running the broadcast-shapes steps given a.[[descriptor]].dimensions and b.[[descriptor]].dimensions.

    1. If that throws an error, re-throw the error.

  6. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and descriptor.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the binary operation op, given a and b.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect a.[[operand]] and b.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  7. Return output.

To broadcast-shapes given shape1 and shape2, run the following steps:
  1. Assert: The type of shape1 and shape2 is sequence of unsigned long.

  2. Let output be the result of invoking the implementation-defined shape broadcast on shape1 and shape2.

    1. If that fails, then throw a "DataError" DOMException.

  3. Return output.

    The most common implementation is that two shapes are compatible, when each of their corresponding dimensions are equal, or one of them is 1. The output shape consists of the maximum of the corresponding dimensions.
The element-wise binary operation algorithms invoke the create element-wise binary operation steps as follows.
The add(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "add", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The sub(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "sub", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The mul(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "mul", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The div(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "div", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The max(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "max", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The min(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "min", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The pow(a, b) method steps are:
  1. Let output be the result of running the create element-wise binary operation given "pow", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.12. Element-wise logical operations

Compare input tensors element-wise and return a uint8 tensor of values 0 or 1 for the comparisons. For single-operand operations, return the logical results of the operation.

The input tensor will be broadcasted according to [numpy-broadcasting-rule]. The rank of the output tensor is the maximum rank of the input tensors.

partial interface MLGraphBuilder {
  MLOperand equal(MLOperand a, MLOperand b);
  MLOperand greater(MLOperand a, MLOperand b);
  MLOperand greaterOrEqual(MLOperand a, MLOperand b);
  MLOperand lesser(MLOperand a, MLOperand b);
  MLOperand lesserOrEqual(MLOperand a, MLOperand b);
  MLOperand not(MLOperand a);
};
Arguments:

Returns: an MLOperand. The output tensor that contains the result of element-wise comparison of the two input tensors.

Operation types:
Although operations greaterOrEqual and lesserOrEqual can each be implemented in terms of operations not, lesser, and greater in other words greater-or-equal(a, b) is not(lesser(a, b)), they are specifically defined to handle NaN cases and for performance reason to avoid double comparisons.
To create element-wise logical operation given op, MLOperand a and an optional MLOperand b, run the following steps:
  1. Assert: op is one of "equal", "greater", "greaterOrEqual", "lesser", "lesserOrEqual", "not".

  2. If op is "not".

    1. If a.[[descriptor]].dataType isn’t "uint8", then throw a "DataError" DOMException.

  3. If op is anything else but "not".

    1. If a.[[descriptor]].dataType is not equal to b.[[descriptor]].dataType, then throw a "DataError" DOMException.

  4. Let descriptor be a new MLOperandDescriptor.

  5. Set descriptor.dataType to "uint8".

  6. Set descriptor.dimensions to the result of running the broadcast-shapes steps given a.[[descriptor]].dimensions and b.[[descriptor]].dimensions.

    1. If that throws an error, re-throw the error.

  7. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and descriptor.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the binary operation op, given a and b.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect a.[[operand]] and b.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  8. Return output.

The element-wise logical operation algorithms invoke the create element-wise logical operation steps as follows.
The equal(a, b) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "equal", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The greater(a, b) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "greater", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The greaterOrEqual(a, b) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "greaterOrEqual", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The lesser(a, b) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "lesser", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The lesserOrEqual(a, b) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "lesserOrEqual", a and b.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The not(a) method steps are:
  1. Let output be the result of running the create element-wise logical operation given "not" and a.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.13. Element-wise unary operations

Compute the element-wise unary operation for input tensor.
partial interface MLGraphBuilder {
  MLOperand abs(MLOperand input);
  MLOperand ceil(MLOperand input);
  MLOperand cos(MLOperand input);
  MLOperand erf(MLOperand input);
  MLOperand exp(MLOperand input);
  MLOperand floor(MLOperand input);
  MLOperand identity(MLOperand input);
  MLOperand log(MLOperand input);
  MLOperand neg(MLOperand input);
  MLOperand reciprocal(MLOperand input);
  MLOperand sin(MLOperand input);
  MLOperand sqrt(MLOperand input);
  MLOperand tan(MLOperand input);
};
Arguments:

Returns: an MLOperand. The output tensor that contains the result of element-wise unary operation of the input tensor. The shape of the output tensor is the same as the shape of input tensor.

Operation types:
To create element-wise unary operation given op and MLOperand input, run the following steps:
  1. Assert: op is one of "abs", "ceil", "cos", "erf", "exp", "floor", "identity", "log", "neg", "reciprocal", "sin", "sqrt", "tan".

  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the unary operation op.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  3. Return output.

The element-wise unary operation algorithms invoke the create element-wise unary operation steps as follows.
The abs(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "abs" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The ceil(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "ceil" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The cos(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "cos" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The erf(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "erf" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The exp(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "exp" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The floor(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "floor" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The identity(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "identity" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The log(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "log" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The neg(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "neg" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reciprocal(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "reciprocal" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The sin(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "sin" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The sqrt(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "sqrt" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The tan(input) method steps are:
  1. Let output be the result of running the create element-wise unary operation given "tan" and input.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.14. elu

Calculate the exponential linear unit function (ELU) on the input tensor element-wise. The calculation follows the expression max(0, x) + alpha * (exp(min(0, x)) - 1).
dictionary MLEluOptions {
  float alpha = 1;
};

partial interface MLGraphBuilder {
  MLOperand elu(MLOperand input, optional MLEluOptions options = {});
  MLActivation elu(optional MLEluOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.add(
          builder.max(builder.constant(0), x),
          builder.mul(
            builder.constant(options.alpha),
            builder.sub(
              builder.exp(builder.min(builder.constant(0), x)),
              builder.constant(1))));
7.7.14.1. elu(input, options)
Arguments:

Returns:

The elu(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the ELU operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.14.2. elu(options)
Arguments:

Returns:

The elu(options) method steps are:
  1. Let op be the result of creating an MLActivation given this, "elu" and options.

  2. Return op.

7.7.15. expand

Expand any dimension of size 1 of the input tensor to a larger size according to the new shape. The expansion is consistent with [numpy-broadcasting-rule]. The input dimensions must have the size of 1 or match the sizes of the corresponding output dimensions according to the new shape.
partial interface MLGraphBuilder {
  MLOperand expand(MLOperand input, sequence<unsigned long> newShape);
};
Arguments:

Returns: an MLOperand. The tensor with expanded size dimensions.

The expand(input, newShape) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. If any of the following steps fail, then throw a "DataError" DOMException.

    1. Let inputDesc be input.[[descriptor]].

    2. If the sequence length of newShape is not equal to the rank of inputDesc, then throw a "DataError" DOMException.

    3. Let outputDesc be a copy of inputDesc.

    4. For each index in the range 0 to the rank of input, exclusive:

      1. Let size be the input.shape()[index].

      2. If size is not equal to 1 and not equal to newShape[index], then throw a "DataError" DOMException.

      3. If size is equal to 1, then let outputDesc.dimensions[index] be newShape[index].

  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and outputDesc.

    2. Make a request to the underlying platform to:

      1. Create platform operator expandImpl for this method, given input and newShape.

      2. Set output.[[operator]] to expandImpl.

      3. Create an platform operand outputImpl to represent output,given output and expandImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input as input to expandImpl.

    4. Connect output.[[operand]] as output to expandImpl.

  3. Return output.

7.7.16. gather

Gather values of the input tensor along an axis according to the indices.
dictionary MLGatherOptions {
  unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand gather(MLOperand input, MLOperand indices, optional MLGatherOptions options = {});
};

MLGatherOptions has the following members:

axis, of type unsigned long, defaulting to 0

An unsigned long scalar specifying the axis along which the gathered values are obtained. Its value must be in the range [0, N-1] where N is the rank of the input tensor.

Arguments:

Returns: an MLOperand. The output N-D tensor of rank equal to the rank of input + the rank of indices - 1.

The gather(input, indices, options) method steps are:
  1. If indices.dataType() is neither "uint32" nor "int64", then throw a "DataError" DOMException.

  2. Let shapeInput be input.[[descriptor]].dimensions and rankInput be the size of shapeInput.

  3. Let shapeIndices be indices.[[descriptor]].dimensions.

  4. Let axis be options.axis.

  5. Let axisSize be input.[[descriptor]].dimensions[axis]

  6. If axis is greater than or equal to rankInput, then throw a "DataError" DOMException.

  7. For each indexvalue of indices:

    1. If index is greater than or equal to axisSize, then throw a "DataError" DOMException.

  8. Let dimCount be zero.

  9. Let rankOutput be zero.

  10. Let shapeOutput be an empty list.

  11. For each sizevalue of shapeInput:

    1. If dimCount is equal to axis then break.

    2. Set shapeOutput[dimCount] to size.

    3. Increment dimCount by one.

  12. Set rankOutput to dimCount.

  13. Let dimCount be zero.

  14. For each sizevalue of shapeIndices:

    1. Set shapeOutput[rankOutput + dimCount] to size.

    2. Increment dimCount by one.

  15. Set rankOutput to rankOutput + dimCount.

  16. Let dimCount be zero.

  17. For each sizevalue of shapeInput:

    1. If dimCount is less than or equal to axis then continue.

    2. Set shapeOutput[rankOutput + dimCount - axis - 1] to size.

    3. Increment dimCount by one.

  18. Let desc be a new MLOperandDescriptor.

  19. Set desc.dimensions to shapeOutput.

  20. Set desc.dataType to input.[[descriptor]].dataType.

  21. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the Gather operation, given input, indices, and options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] and indices.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  22. Return output.

Examples of how gather works in different slicing schemes.
// input of shape [4,3]:
//   [[ 0,  1,  2],
//    [10, 11, 12], 
//    [20, 21, 22], 
//    [30, 31, 32]]
const input = builder.constant(
{ dimensions: [4,3] }, new Float32Array([0,1,2,10,11,12,20,21,22,30,31,32]));

const indices1 = builder.constant(
{ dataType: 'uint32', dimensions: [2] }, new Uint32Array([3,1]));

const indices2 = builder.constant(
{ dataType: 'uint32', dimensions: [3] }, new Uint32Array([2,1,1]));

const indices3 = builder.constant(
{ dataType: 'uint32', dimensions: [2,2] }, new Uint32Array([0,1,1,2]));

// axis = 0 (default)
// indices of shape [2]: 
//   [3,1]
// output of shape [2,3]:
//   [[30, 31, 32], 
//    [10, 11, 12]]
const output1 = builder.gather(input, indices1);

// axis = 1
// indices of shape [3]:
//   [2,1,1]
// output of shape [4,3]:
//   [[ 2,  1,  1],
//    [12, 11, 11], 
//    [22, 21, 21],
//    [32, 31, 31]]
const output2 = builder.gather(input, indices2, { axis: 1 });

// axis = 1
// indices of shape [2,2]: 
//   [[0, 1], 
//    [1, 2]]
// output of shape [4,2,2]:
//   [[[ 0,  1], [ 1,  2]],
//    [[10, 11], [11, 12]],
//    [[20, 21], [21, 22]],
//    [[30, 31], [31, 32]]]
const output3 = builder.gather(input, indices3, { axis: 1 });

7.7.17. gemm

Calculate the general matrix multiplication of the Basic Linear Algebra Subprograms. The calculation follows the expression alpha * A * B + beta * C, where A is a 2-D tensor with shape [M, K] or [K, M], B is a 2-D tensor with shape [K, N] or [N, K], and C is broadcastable to the shape [M, N]. A and B may optionally be transposed prior to the calculation.
dictionary MLGemmOptions {
  MLOperand c;
  float alpha = 1.0;
  float beta = 1.0;
  boolean aTranspose = false;
  boolean bTranspose = false;
};

partial interface MLGraphBuilder {
  MLOperand gemm(MLOperand a, MLOperand b, optional MLGemmOptions options = {});
};

MLGemmOptions has the following members:

c, of type MLOperand

An MLOperand. Specifies the third input tensor. It is either a scalar, or of the shape that is unidirectionally broadcastable to the shape [M, N] according to [numpy-broadcasting-rule]. When it is not specified, the computation is done as if c is a scalar 0.0.

alpha, of type float, defaulting to 1.0

A float scalar multiplier for the first input.

beta, of type float, defaulting to 1.0

A float scalar multiplier for the third input c.

aTranspose, of type boolean, defaulting to false

A boolean indicating if the first input should be transposed prior to calculating the output.

bTranspose, of type boolean, defaulting to false

A boolean indicating if the second input should be transposed prior to calculating the output.

Arguments:

Returns: an MLOperand. The output 2-D tensor of shape [M, N] that contains the calculated product of all the inputs.

The gemm(a, b, options) method steps are:
  1. Let shapeA be a.[[descriptor]].dimensions and sizeA the size of shapeA.

  2. Let shapeB be b.[[descriptor]].dimensions and sizeB the size of shapeB.

  3. If sizeA is not 2 or sizeB is not 2, then throw a "DataError" DOMException.

  4. If options.aTranspose is true, then let shapeA be the reverse array of shapeA.

  5. If options.bTranspose is true, then let shapeB be the reverse array of shapeB.

  6. If shapeA[1] is not equal to shapeB[0], then throw a "DataError" DOMException.

  7. If options.c exists and is not unidirectionally broadcastable to the shape [shapeA[0], shapeB[1]] according to the [numpy-broadcasting-rule], then throw a "DataError" DOMException.

    Type compatibility between a, b and options.c can be also checked.
  8. Let desc be a new MLOperandDescriptor.

  9. Set desc.dimensions to the list « shapeA[0], shapeB[1] ».

  10. Set desc.dataType to a.[[descriptor]].dataType.

  11. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the GEMM operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect a.[[operand]] and b.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  12. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
if (options.aTranspose)
  a = builder.transpose(a);

if (options.bTranspose)
  b = builder.transpose(b);

let ab = builder.matmul(builder.mul(builder.constant(options.alpha), a), b);
return (c ? builder.add(ab, builder.mul(builder.constant(options.beta), c)) : ab);

7.7.18. gru

Gated Recurrent Unit [GRU] recurrent network uses an update, reset, and new gate to compute the output state that rolls into the output across the temporal sequence of the network.
enum MLGruWeightLayout {
  "zrn",  // update-reset-new gate ordering
  "rzn"   // reset-update-new gate ordering
};

enum MLRecurrentNetworkDirection {
  "forward",
  "backward",
  "both"
};

dictionary MLGruOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand initialHiddenState;
  boolean resetAfter = true;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> gru(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                          unsigned long steps, unsigned long hiddenSize,
                          optional MLGruOptions options = {});
};

MLGruOptions has the following members:

bias, of type MLOperand

An MLOperand. Specifies the 2-D input bias tensor of shape [numDirections, 3 * hiddenSize]. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the layout argument.

recurrentBias, of type MLOperand

An MLOperand. Specifies the 2-D recurrent bias tensor of shape [numDirections, 3 * hiddenSize]. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the layout argument.

initialHiddenState, of type MLOperand

An MLOperand. The 3-D initial hidden state tensor of shape [numDirections, batchSize, hiddenSize]. When not specified, implementations SHOULD use a tensor filled with zero.

resetAfter, of type boolean, defaulting to true

A boolean indicating whether to apply the reset gate after or before matrix multiplication. The default value is true.

returnSequence, of type boolean, defaulting to false

A boolean indicating whether to also return the entire sequence with every output from each time step in it in addition to the output of the last time step. The default value is false.

direction, of type MLRecurrentNetworkDirection, defaulting to "forward"

An MLRecurrentNetworkDirection. Specifies the processing direction of the input sequence. When set to "both", the size of the first dimension of the weight and the bias tensor shapes must be 2, and the input is processed in both directions.

layout, of type MLGruWeightLayout, defaulting to "zrn"

An MLGruWeightLayout. The ordering of the weight and bias vectors for the internal gates of GRU, specifically the update (z), reset (r), and new (n) gate, as indicated in the second dimension of the weight and bias tensor shape. When not specified, the default layout is "zrn".

activations, of type sequence<MLActivation>

A sequence of MLActivation. Specifies a pair of activation functions with the first function used for the update and reset gate, and the second used for the new gate. When not specified, implementations SHOULD use the the pair of sigmoid ("sigmoid") and the hyperbolic tangent ("tanh") functions, respectively.

Arguments:

Returns: a sequence of MLOperand. The first element of the sequence is a 3-D tensor of shape [numDirections, batchSize, hiddenSize], the cell output from the last time step of the network. Additionally, if options.returnSequence is set to true, the second element is the 4-D output tensor of shape [steps, numDirections, batchSize, hiddenSize] containing every cell outputs from each time step in the temporal sequence.

The gru(input, weight, recurrentWeight, steps, hiddenSize, options) method steps are:
  1. If the rank of input or weight or recurrentWeight is not 3, then throw a "DataError" DOMException.

  2. If options.bias exists.

    1. If options.bias.[[descriptor]].dimensions[1] is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  3. If options.recurrentBias exists.

    1. If options.recurrentBias.[[descriptor]].dimensions[1] is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  4. If options.initialHiddenState exists.

    1. If its rank is not 3, then throw a "DataError" DOMException.

  5. If options.activations exists and its size is not 2, then throw a TypeError.

  6. If steps is not equal to input.[[descriptor]].dimensions[0], then throw a TypeError.

  7. Let output be an empty sequence of MLOperand objects.

  8. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for "gru", given weight, recurrentWeight, steps, hiddenSize and options as parameters.

    2. Connect input.[[operand]] as input to opImpl.

    3. Connect output as output to opImpl.

  9. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follows. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
function squeeze(builder, op) {
  return builder.reshape(op, op.shape().remove(0));
}

const numDirections = (options.direction == "both" ? 2 : 1);
let hiddenState = options.initialHiddenState;

if (!hiddenState) {
  const desc = { dataType: 'float32', dimensions: [numDirections, 1, hiddenSize] };
  const totalSize = numDirections * hiddenSize;
  hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
}

let sequence = null;
let currentWeight = [];
let currentRecurrentWeight = [];
let currentBias = [];
let currentRecurrentBias = [];

for (let dir = 0; dir < numDirections; ++dir) {
  currentWeight.push(squeeze(builder, builder.slice(weight, [dir, 0, 0], [1, 3 * hiddenSize, inputSize])));
  currentRecurrentWeight.push(squeeze(builder, builder.slice(recurrentWeight, [dir, 0, 0], [1, 3 * hiddenSize, hiddenSize])));
  currentBias.push(options.bias ? (squeeze(builder, builder.slice(options.bias, [dir, 0], [1, 3 * hiddenSize]))) : null);
  currentRecurrentBias.push(options.recurrentBias ?
    (squeeze(builder, builder.slice(options.recurrentBias, [dir, 0], [1, 3 * hiddenSize]))) : null);
}

for (let step = 0; step < steps; ++step) {
  let currentHidden = [];
  let currentOutput = null;

  for (let dir = 0; dir < numDirections; ++dir) {
    currentHidden.push(squeeze(builder, builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize])));
  }

  for (let dir = 0; dir < numDirections; ++dir) {
    let slice = (dir == 1 || options.direction == "backward" ? steps - step - 1 : step);
    let currentInput = squeeze(builder, builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]));

    let result = builder.reshape(
      builder.gruCell(
        currentInput, currentWeight[dir], currentRecurrentWeight[dir],
        currentHidden[dir], hiddenSize, { bias: currentBias[dir],
        recurrentBias: currentRecurrentBias[dir], resetAfter: options.resetAfter,
        layout: options.layout, activations: options.activations }),
      [1, null, hiddenSize]);

    currentOutput = (currentOutput ? builder.concat([currentOutput, result], 0) : result);
  }

  hiddenState = currentOutput;

  if (options.returnSequence) {
    currentOutput = builder.reshape(currentOutput, [1, numDirections, null, hiddenSize]);
    sequence = (sequence ? builder.concat([sequence, currentOutput], 0) : currentOutput);
  }
}

return (sequence ? [hiddenState, sequence] : [hiddenState]);

7.7.19. gruCell

A single time step of the Gated Recurrent Unit [GRU] recurrent network using an update gate and a reset gate to compute the hidden state that rolls into the output across the temporal sequence of a recurrent network.
dictionary MLGruCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  boolean resetAfter = true;
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  MLOperand gruCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                    MLOperand hiddenState, unsigned long hiddenSize,
                    optional MLGruCellOptions options = {});
};

MLGruCellOptions has the following members:

bias, of type MLOperand

An MLOperand. Specifies the 1-D input bias tensor of shape [3 * hiddenSize]. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the layout argument.

recurrentBias, of type MLOperand

An MLOperand. Specifies the 1-D recurrent bias tensor of shape [3 * hiddenSize]. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the layout argument.

resetAfter, of type boolean, defaulting to true

A boolean indicating whether to apply the reset gate after or before matrix multiplication. The default value is true.

layout, of type MLGruWeightLayout, defaulting to "zrn"

An MLGruWeightLayout. The ordering of the weight and bias vectors for the internal gates of GRU, specifically the update (z), reset (r), and new (n) gate, as indicated in the second dimension of the weight and bias tensor shape. When not specified, the default layout is "zrn".

activations, of type sequence<MLActivation>

A sequence of MLActivation. Specifies a pair of activation functions with the first function used for the update and reset gate, and the second used for the new gate. When not specified, implementations SHOULD use the the pair of sigmoid ("sigmoid") and the hyperbolic tangent ("tanh") functions, respectively.

Arguments:

Returns: an MLOperand. The 2-D tensor of shape [batchSize, hiddenSize], the cell output hidden state of a single time step of the recurrent network.

The gruCell(input, weight, recurrentWeight, hiddenState, hiddenSize, options) method steps are:
  1. If the rank of input or weight or recurrentWeight or hiddenState is not 2, then throw a "DataError" DOMException.

  2. If weight.[[descriptor]].dimensions[0] is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  3. If recurrentWeight.[[descriptor]].dimensions[0] is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  4. If options.bias exists:

    1. If its rank is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  5. If options.recurrentBias exists:

    1. If its rank is not equal to 3 * hiddenSize, then throw a "DataError" DOMException.

  6. If options.activations exists and its size is not 2, then throw a TypeError.

  7. Let desc be a new MLOperandDescriptor.

  8. Set desc.dimensions to the list « input.dimensions[0], hiddenSize ».

  9. Set desc.dataType to input.[[descriptor]].dataType.

  10. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for "gruCell", given weight, recurrentWeight, hiddenState, hiddenSize and options as parameters.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  11. Return output.

The behavior of this operation can be generically emulated via other operations as shown below, when the weight layout is the default "zrn" layout, and the activation functions of the update/reset gate and new gate are of the operator types sigmoid and tanh respectively.
const one = builder.constant(1);
const zero = builder.constant(0);

// update gate (z)
let z = builder.sigmoid(
  builder.add(
    builder.add(
      (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
      (options.recurrentBias ? builder.slice(options.recurrentBias, [0], [hiddenSize]) : zero)
      ),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(builder.slice(weight, [0, 0], [hiddenSize, inputSize]))
        ),
      builder.matmul(
        hiddenState,
        builder.transpose(builder.slice(recurrentWeight, [0, 0], [hiddenSize, hiddenSize]))
        )
      )
    )
  );

// reset gate (r)
let r = builder.sigmoid(
  builder.add(
    builder.add(
      (options.bias ? builder.slice(options.bias, [hiddenSize], [hiddenSize]) : zero),
      (options.recurrentBias ? builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) : zero)
      ),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))
        ),
      builder.matmul(
        hiddenState,
        builder.transpose(builder.slice(recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize]))
        )
      )
    )
  );

// new gate (n)
let n;
if (resetAfter) {
  n = builder.tanh(
    builder.add(
      (options.bias ? builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) : zero),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))
          ),
        builder.mul(
          r,
          builder.add(
            (options.recurrentBias ? builder.slice(options.recurrentBias, [2 * hiddenSize], [hiddenSize]) : zero),
            builder.matmul(
              hiddenState,
              builder.transpose(builder.slice(recurrentWeight, [2 * hiddenSize, 0], [hiddenSize, hiddenSize]))
              )
            )
          )
        )
      )
    );
}
else {
  n = builder.tanh(
    builder.add(
      builder.add(
        (options.bias ? builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) : zero),
        (options.recurrentBias ? builder.slice(options.recurrentBias, [2 * hiddenSize], [hiddenSize]) : zero)
        ),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))
          ),
        builder.matmul(
          builder.mul(r, hiddenState),
          builder.transpose(builder.slice(recurrentWeight, [2 * hiddenSize, 0], [hiddenSize, hiddenSize]))
          )
        )
      )
    );
}

// compute the new hidden state
return builder.add(builder.mul(z, hiddenState), builder.mul(n, builder.sub(one, z)));

7.7.20. hardSigmoid

Calculate the non-smooth hard sigmoid function on the input tensor, used instead of the sigmoid function for faster computation.
dictionary MLHardSigmoidOptions {
  float alpha = 0.2;
  float beta = 0.5;
};

partial interface MLGraphBuilder {
  MLOperand hardSigmoid(MLOperand input, optional MLHardSigmoidOptions options = {});
  MLActivation hardSigmoid(optional MLHardSigmoidOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.max(
           builder.min(
               builder.add(
                   builder.mul(builder.constant(options.alpha), x),
                   builder.constant(options.beta)),
               builder.constant(1)),
           builder.constant(0));

MLHardSigmoidOptions has the following members:

alpha, of type float, defaulting to 0.2

A float scalar multiplier. The default value is 0.2.

beta, of type float, defaulting to 0.5

A float scalar addition. The default value is 0.5.

7.7.20.1. hardSigmoid(input, options)
Arguments:

Returns:

The hardSigmoid(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the hard sigmoid operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.20.2. hardSigmoid(options)
Arguments:

Returns:

The hardSigmoid(options) method steps are:
  1. Let op be the result of creating an MLActivation given this, "hardSigmoid" and options.

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.21. hardSwish

Computes the nonlinear function y = x * max(0, min(6, (x + 3))) / 6 that is introduced by [MobileNetV3] on the input tensor element-wise.
partial interface MLGraphBuilder {
  MLOperand hardSwish(MLOperand input);
  MLActivation hardSwish();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.div(
           builder.mul(
               x,
               builder.max(
                   builder.constant(0),
                   builder.min(
                       builder.constant(6),
                       builder.add(x, builder.constant(3))))),
           builder.constant(6));
7.7.21.1. hardSwish(input)
Arguments:

Returns:

The hardSwish(input) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the hard-swish operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.21.2. hardSwish()
Arguments:

Returns:

The hardSwish() method steps are:
  1. Let op be the result of creating an MLActivation given this and "hardSwish".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.22. input

Create a named MLOperand based on a descriptor, that can be used as an input.
Arguments: Returns:: an MLOperand object.
The input(name, descriptor) method steps are:
The permissions and context validity have been checked by § 7.7.1 MLGraphBuilder constructor steps.
  1. If name is empty, then throw a TypeError.

  2. Assert: If descriptor.dimensions does not exist, then descriptor defines a scalar input.

  3. If descriptor.dimensions exists:

    1. If checking dimensions given descriptor.dataType and descriptor.dimensions returns false, then throw a "DataError" DOMException.

    2. If the byte length of descriptor is not supported by the underlying platform, then throw a "DataError" DOMException.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let operand be the result of creating an MLOperand given this and descriptor.

    2. Set operand.[[name]] to name.

    3. Make a request to the underlying platform to:

      1. Create an implementation-defined platform input operand operandImpl given descriptor.

      2. Set operand.[[operand]] to operandImpl.

      3. Register operand as an input.

  5. Return operand.

7.7.23. instanceNormalization

Normalize the input using [Instance-Normalization]. Unlike § 7.7.3 batchNormalization where the mean and variance values used in the normalization are computed across all the samples in the batch dimension while the model is trained, the mean and variance values used in the instance normalization are computed on the fly for each input feature of each individual sample in the batch.
dictionary MLInstanceNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  float epsilon = 1e-5;
  MLInputOperandLayout layout = "nchw";
};

partial interface MLGraphBuilder {
  MLOperand instanceNormalization(MLOperand input,
                                optional MLInstanceNormalizationOptions options = {});
};

The MLInstanceNormalizationOptions members are:

scale, of type MLOperand

An MLOperand. Specifies the 1-D tensor of the scaling values whose size is equal to the number of channels, i.e. the size of the feature dimension of the input. For example, for an input tensor with nchw layout, the size is equal to input.[[descriptor]].dimensions[1].

bias, of type MLOperand

An MLOperand. Specifies the 1-D tensor of the bias values whose size is equal to the size of the feature dimension of the input. For example, for an input tensor with nchw layout, the size is equal to input.[[descriptor]].dimensions[1].

epsilon, of type float, defaulting to 1e-5

A float scalar. Specifies a small value to prevent computational error due to divide-by-zero.

layout, of type MLInputOperandLayout, defaulting to "nchw"

An MLInputOperandLayout. Specifies the layout format of the input.

Arguments:

Returns: an MLOperand. The instance-normalized 4-D tensor of the same shape as input.

The instanceNormalization(input, options) method steps are:
  1. If the rank of input is not 4, then throw a "DataError" DOMException.

  2. If the rank of options.scale is not equal to 1, then throw a "DataError" DOMException.

  3. If the rank of options.bias is not equal to 1, then throw a "DataError" DOMException.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the instance normalization operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  5. Return output.

The behavior of this operation when the input tensor is 4-D of the "nchw" layout can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
// The reduction of the mean and variance values happens over the spatial dimensions of the input
// e.g. axis 2 and 3 of the input tensor.
const reduceOptions = { axes: [2,3], keepDimensions: true };
const mean = builder.reduceMean(input, reduceOptions);
const variance = builder.reduceMean(
  builder.pow(
    builder.sub(input, mean),
    buider.constant(2)),
  reduceOptions
  );

// The scale and bias values are applied per input feature
// e.g. axis 1 of the input tensor.
const shape = [1,null,1,1];
return builder.add(
  builder.mul(
    builder.reshape(options.scale, shape),
    builder.div(
      builder.sub(input, mean),
      buidler.sqrt(builder.add(variance, options.epsilon))
      )
    ),
  builder.reshape(options.bias, shape)
  );

7.7.24. layerNormalization

Normalize the input using [Layer-Normalization]. Unlike § 7.7.3 batchNormalization where the mean and variance values are computed across all the samples in the batch dimension while the model is trained, and in § 7.7.23 instanceNormalization where the mean and variance values are computed on the fly for each input feature of each individual sample in the batch, the means and variance values of the layer normalization are computed on the fly across all the input features of each individual sample in the batch.
dictionary MLLayerNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  sequence<unsigned long> axes;
  float epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand layerNormalization(MLOperand input, optional MLLayerNormalizationOptions options = {});
};

The MLLayerNormalizationOptions members are:

scale, of type MLOperand

An MLOperand. Specifies the N-D tensor of the scaling values whose shape is determined by the axes member in that each value in axes indicates the dimension of the input tensor with scaling values. For example, for an axes values of [1,2,3], the shape of this tensor is the list of the corresponding sizes of the input dimension 1, 2 and 3. When this member is not present, the scaling value is assumed to be 1.

bias, of type MLOperand

An MLOperand. Specifies the N-D tensor of the bias values whose shape is determined by the axes member in that each value in axes indicates the dimension of the input tensor with bias values. For example, for an axes values of [1,2,3], the shape of this tensor is the list of the corresponding sizes of the input dimension 1, 2 and 3. When this member is not present, the bias value is assumed to be 0.

axes, of type sequence<unsigned long>

A sequence of unsigned long. The indices to the input dimensions to reduce. When this member is not present, it is assumed to be [1,2,3] that is, the reduction for the mean and variance values are calculated across all the input features for each individual sample in the batch.

epsilon, of type float, defaulting to 1e-5

A float scalar. Specifies a small value to prevent computational error due to divide-by-zero.

Arguments:

Returns: an MLOperand. The layer-normalized N-D tensor of the same shape as input.

The layerNormalization(input, options) method steps are:
  1. If the rank of options.scale is not equal to the size of options.axes, then throw a "DataError" DOMException.

  2. If the rank of options.bias is not equal to the size of options.axes, then throw a "DataError" DOMException.

  3. For each index in the range 0 to the size of options.axes, exclusive:

    1. Let axis be options.axes[index].

    2. If axis is greater or equal to the size of input.[[descriptor]].dimensions, then throw a "DataError" DOMException.

    3. Let size be input.[[descriptor]].dimensions[axis].

    4. If options.scale.[[descriptor]].dimensions[index] is not equal to size, then throw a "DataError" DOMException.

    5. If options.bias.[[descriptor]].dimensions[index] is not equal to size, then throw a "DataError" DOMException.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the instance normalization operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  5. Return output.

The behavior of this operation when the axes parameter is set to [1,2,3] can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
// The reduction of the mean and variance values happens over the spatial dimensions 
// across all the input features (i.e. all channels) of the input tensor.
const reduceOptions = { axes: [1,2,3], keepDimensions: true };
const mean = builder.reduceMean(input, reduceOptions);
const variance = builder.reduceMean(
  builder.pow(
    builder.sub(input, mean),
    buider.constant(2)),
  reduceOptions
  );

// The scale and bias tensors are of the shape of the input dimensions specified 
// by the values in the axes parameter (i.e. [1,2,3]).
return builder.add(
  builder.mul(
    options.scale,
    builder.div(
      builder.sub(input, mean),
      buidler.sqrt(builder.add(variance, options.epsilon))
      )
    ),
  options.bias
  );

7.7.25. leakyRelu

Calculate the leaky version of rectified linear function on the input tensor element-wise. The calculation follows the expression max(0, x) + alpha ∗ min(0, x).
dictionary MLLeakyReluOptions {
  float alpha = 0.01;
};

partial interface MLGraphBuilder {
  MLOperand leakyRelu(MLOperand input, optional MLLeakyReluOptions options = {});
  MLActivation leakyRelu(optional MLLeakyReluOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.add(builder.max(builder.constant(0), x),
          builder.mul(builder.constant(options.alpha), builder.min(builder.constant(0), x)));

MLLeakyReluOptions has the following members:

alpha, of type float, defaulting to 0.01

A float scalar multiplier. The default value is 0.01.

7.7.25.1. leakyRelu(input, options)
Arguments:

Returns:

The leakyRelu(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the Leaky RELU operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.25.2. leakyRelu(options)
Arguments:

Returns:

The leakyRelu(options) method steps are:
  1. Let op be the result of creating an MLActivation given this, "leakyRelu" and options.

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.26. linear

Calculate a linear function y = alpha * x + beta on the input tensor.
dictionary MLLinearOptions {
  float alpha = 1;
  float beta = 0;
};

partial interface MLGraphBuilder {
  MLOperand linear(MLOperand input, optional MLLinearOptions options = {});
  MLActivation linear(optional MLLinearOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.add(
          builder.mul(x, builder.constant(options.alpha)),
          builder.constant(options.beta));

MLLinearOptions has the following members:

alpha, of type float, defaulting to 1

A float scalar multiplier. The default value is 1.

beta, of type float, defaulting to 0

A float scalar addition. The default value is 0.

7.7.26.1. linear(input, options)
Arguments:

Returns:

The linear(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the linear operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.26.2. linear(options)
Arguments:

Returns:

The linear(options) method steps are:
  1. Let op be the result of creating an MLActivation given this, "linear" and options.

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.27. lstm

Long Short-Term Memory [LSTM] recurrent network uses an input, output, forget, and cell gate to compute the output state that rolls into the output across the temporal sequence of the network.
enum MLLstmWeightLayout {
  "iofg", // input-output-forget-cell gate ordering
  "ifgo"  // input-forget-cell-output gate ordering
};

dictionary MLLstmOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLOperand initialHiddenState;
  MLOperand initialCellState;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstm(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                           unsigned long steps, unsigned long hiddenSize,
                           optional MLLstmOptions options = {});
};

MLLstmOptions has the following members:

bias, of type MLOperand

An MLOperand. Specifies the 2-D input bias tensor of shape [numDirections, 4 * hiddenSize]. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to layout.

recurrentBias, of type MLOperand

An MLOperand. Specifies the 2-D recurrent bias tensor of shape [numDirections, 4 * hiddenSize]. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to layout.

peepholeWeight, of type MLOperand

An MLOperand. Specifies the 2-D weight tensor for peepholes of shape [numDirections, 3 * hiddenSize]. The pack ordering of the weight vectors is for the input (i), output (o), and forget (f) gate, respectively.

initialHiddenState, of type MLOperand

An MLOperand. Specifies the 3-D initial hidden state tensor of shape [numDirections, batchSize, hiddenSize]. When not specified, implementations SHOULD use a tensor filled with zero.

initialCellState, of type MLOperand

An MLOperand. Specifies the 3-D initial hidden state tensor of shape [numDirections, batchSize, hiddenSize]. When not specified, implementations SHOULD use a tensor filled with zero.

returnSequence, of type boolean, defaulting to false

A boolean indicating whether to also return the entire sequence with every output from each time step in it in addition to the output of the last time step.

direction, of type MLRecurrentNetworkDirection, defaulting to "forward"

An MLRecurrentNetworkDirection. Specifies the processing direction of the input sequence. When set to "both", the size of the first dimension of the weight and the bias tensor shapes must be 2, and the input is processed in both directions.

layout, of type MLLstmWeightLayout, defaulting to "iofg"

An MLLstmWeightLayout. The ordering of the weight and bias vectors for the internal gates of LSTM, specifically the input (i), output (o), forget (f), and cell (g) gate, as indicated in the first dimension of the weight and bias tensor shapes. When not specified, the default layout is "iofg".

activations, of type sequence<MLActivation>

A sequence of MLActivation. A sequence of three activation functions, the first one is used for the input (i), forget (f), and output (o) gate, the second one is used for the cell (g) gate, and the last used for filtering the output cell state before combining it with the result of the output gate to form the output hidden state. When not specified, implementations SHOULD use the sequence of the sigmoid function ("sigmoid") followed by two hyperbolic tangent functions ("tanh") respectively.

Arguments:

Returns: a sequence of MLOperand. The first element of the sequence is a 3-D tensor of shape [numDirections, batchSize, hiddenSize], the output hidden state from the last time step of the network. The second element is a 3-D tensor of shape [numDirections, batchSize, hiddenSize], the output cell state from the last time step of the network. Additionally, if options.returnSequence is set to true, the third element is the 4-D output tensor of shape [steps, numDirections, batchSize, hiddenSize] containing every output from each time step in the temporal sequence.

The lstm(input, weight, recurrentWeight, steps, hiddenSize, options) method steps are:
  1. Let numDirections be 1 if options.direction is "forward", or otherwise let it be 2.

    The shape of input, weight or recurrentWeight could be also checked here.
  2. If input.[[descriptor]].dimensions[0] is not equal to steps, then throw a "DataError" DOMException.

  3. Let batchSize be input.[[descriptor]].dimensions[1].

  4. If options.bias exists:

    1. If its rank is not 2, then throw a "DataError" DOMException.

    2. If options.bias.[[descriptor]].dimensions[0] is not numDirections, then throw a "DataError" DOMException.

    3. If options.bias.[[descriptor]].dimensions[1] is not 4 * hiddenSize, then throw a "DataError" DOMException.

  5. If options.recurrentBias exists:

    1. If its rank is not 2, then throw a "DataError" DOMException.

    2. If options.recurrentBias.[[descriptor]].dimensions[0] is not numDirections, then throw a "DataError" DOMException.

    3. If options.recurrentBias.[[descriptor]].dimensions[1] is not 4 * hiddenSize, then throw a "DataError" DOMException.

  6. If options.peepholeWeight exists:

    1. If its rank is not 2, then throw a "DataError" DOMException.

    2. If options.peepholeWeight.[[descriptor]].dimensions[0] is not numDirections, then throw a "DataError" DOMException.

    3. If options.peepholeWeight.[[descriptor]].dimensions[1] is not 4 * hiddenSize, then throw a "DataError" DOMException.

  7. If options.initialHiddenState exists:

    1. If its rank is not 3, then throw a "DataError" DOMException.

    2. If options.initialHiddenState.[[descriptor]].dimensions[0] is not numDirections, then throw a "DataError" DOMException.

    3. If options.initialHiddenState.[[descriptor]].dimensions[1] is not equal to batchSize, then throw a "DataError" DOMException.

    4. If options.initialHiddenState.[[descriptor]].dimensions[2] is not hiddenSize, then throw a "DataError" DOMException.

  8. If options.initialCellState exists:

    1. If its rank is not 3, then throw a "DataError" DOMException.

    2. If options.initialCellState.[[descriptor]].dimensions[0] is not numDirections, then throw a "DataError" DOMException.

    3. If options.initialCellState.[[descriptor]].dimensions[1] is not equal to batchSize, then throw a "DataError" DOMException.

    4. If options.initialCellState.[[descriptor]].dimensions[2] is not hiddenSize, then throw a "DataError" DOMException.

  9. If options.activations exists:

    1. If its size is not 3, then throw a TypeError.

  10. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let desc be a new MLOperandDescriptor.

    2. Set desc.dimensions to the list « numDirections, batchSize, hiddenSize ».

    3. Set desc.dataType to input.[[descriptor]].dataType.

    4. Let output0 be the result of creating an MLOperand given this and desc.

    5. Let output1 be the result of creating an MLOperand given this and desc.

    6. Set desc.dimensions to the list « steps, numDirections, batchSize, hiddenSize ».

    7. If options.returnSequence is set to true:

      1. Let output2 be the result of creating an MLOperand given this and desc.

      2. Let output be the list « output0, output1, output2 ».

    8. Otherwise, let output be the list « output0, output1 ».

    9. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the LSTM operation, given weight, recurrentWeight, steps, hiddenSize and options.

      2. Set output0.[[operator]], output1.[[operator]] and output2.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output0.[[operand]], output1.[[operand]] and output2.[[operand]] to outputImpl.

    10. Connect input.[[operand]] as input to opImpl.

    11. Connect output as output to opImpl.

  11. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
function squeeze(builder, op) {
  return builder.reshape(op, op.shape().remove(0));
}

const numDirections = (options.direction == "both" ? 2 : 1);
let hiddenState = options.initialHiddenState;
let cellState = options.initialCellState;

if (!hiddenState) {
  const desc = { dataType: 'float32', dimensions: [numDirections, 1, hiddenSize] };
  const totalSize = numDirections * hiddenSize;
  hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
}

if (!cellState) {
  const desc = { dataType: 'float32', dimensions: [numDirections, 1, hiddenSize] };
  const totalSize = numDirections * hiddenSize;
  cellState = builder.constant(desc, new Float32Array(totalSize).fill(0));
}

let sequence = null;
let currentWeight = [];
let currentRecurrentWeight = [];
let currentBias = [];
let currentRecurrentBias = [];
let currentPeepholeWeight = [];

for (let dir = 0; dir < numDirections; ++dir) {
  currentWeight.push(squeeze(builder, builder.slice(weight, [dir, 0, 0], [1, 4 * hiddenSize, inputSize])));
  currentRecurrentWeight.push(squeeze(builder, builder.slice(recurrentWeight, [dir, 0, 0], [1, 4 * hiddenSize, hiddenSize])));
  currentBias.push(options.bias ? (squeeze(builder, builder.slice(options.bias, [dir, 0], [1, 4 * hiddenSize]))) : null);
  currentRecurrentBias.push(options.recurrentBias ?
    (squeeze(builder, builder.slice(options.recurrentBias, [dir, 0], [1, 4 * hiddenSize]))) : null);
  currentPeepholeWeight.push(options.peepholeWeight ?
    (squeeze(builder, builder.slice(options.peepholeWeight, [dir, 0], [1, 3 * hiddenSize]))) : null);
}

for (let step = 0; step < steps; ++step) {
  let currentHidden = [];
  let currentCell = [];
  let nextHidden = null;
  let nextCell = null;

  for (let dir = 0; dir < numDirections; ++dir) {
    currentHidden.push(squeeze(builder, builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize])));
    currentCell.push(squeeze(builder, builder.slice(cellState, [dir, 0, 0], [1, batchSize, hiddenSize])));
  }

  for (let dir = 0; dir < numDirections; ++dir) {
    let slice = (dir == 1 || options.direction == "backward" ? steps - step - 1 : step);
    let currentInput = squeeze(builder, builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]));

    let results = builder.lstmCell(
      currentInput, currentWeight[dir], currentRecurrentWeight[dir],
      currentHidden[dir], currentCell[dir], hiddenSize, { bias: currentBias[dir],
      recurrentBias: currentRecurrentBias[dir], peepholeWeight: currentPeepholeWeight[dir],
      layout: options.layout, activations: options.activations });

    let output = builder.reshape(results[0], [1, null, hiddenSize]);
    let cell = builder.reshape(results[1], [1, null, hiddenSize]);

    nextHidden = (nextHidden ? builder.concat([nextHidden, output], 0) : output);
    nextCell = (nextCell ? builder.concat([nextCell, cell], 0) : cell);
  }

  hiddenState = nextHidden;
  cellState = nextCell;

  if (options.returnSequence) {
    nextHidden = builder.reshape(nextHidden, [1, numDirections, null, hiddenSize]);
    sequence = (sequence ? builder.concat([sequence, nextHidden], 0) : nextHidden);
  }
}

return (sequence ? [hiddenState, cellState, sequence] : [hiddenState, cellState]);

7.7.28. lstmCell

A single time step of the Long Short-Term Memory [LSTM] recurrent network using a cell state, an input, output, and forget gate to compute the cell state and the hidden state of the next time step that rolls into the output across the temporal sequence of the network.
dictionary MLLstmCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstmCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                               MLOperand hiddenState, MLOperand cellState, unsigned long hiddenSize,
                               optional MLLstmCellOptions options = {});
};

MLLstmCellOptions has the following members:

bias, of type MLOperand

An MLOperand. The 1-D input bias tensor of shape [4 * hiddenSize]. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to the layout argument.

recurrentBias, of type MLOperand

An MLOperand. The 1-D recurrent bias tensor of shape [4 * hiddenSize]. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to the layout argument.

peepholeWeight, of type MLOperand

An MLOperand. The 1-D weight tensor for peepholes of shape [3 * hiddenSize]. The pack ordering of the weight vectors is for the input (i), output (o), and forget (f) gate, respectively.

layout, of type MLLstmWeightLayout, defaulting to "iofg"

An MLLstmWeightLayout. The ordering of the weight and bias vectors for the internal gates of LSTM, specifically the input (i), output (o), forget (f), and cell (g) gate, as indicated in the first dimension of the weight and bias tensor shapes. When not specified, the default layout is "iofg".

activations, of type sequence<MLActivation>

A sequence of MLActivation. A sequence of three activation functions, the first one is used for the input (i), forget (f), and output (o) gate, the second one is used for the cell (g) gate, and the last used for filtering the output cell state before combining it with the result of the output gate to form the output hidden state. When not specified, they are assumed to be of the sigmoid function ("sigmoid") followed by two hyperbolic tangent functions ("tanh") respectively.

Arguments:

Returns: a sequence of MLOperand. The first element of the sequence is the output hidden state of the current time step of the recurrent network. The following element is the output cell state. Both elements are 2-D tensors of shape [batchSize, hiddenSize].

The lstmCell(input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, options) method steps are:
  1. If the rank of input, weight, recurrentWeight, hiddenState or cellState is not 2, then throw a "DataError" DOMException.

  2. Let batchSize be input.[[descriptor]].dimensions[0].

  3. If options.bias exists:

    1. If its rank is not 1, then throw a "DataError" DOMException.

    2. If options.bias.[[descriptor]].dimensions[0] is not 4 * hiddenSize, then throw a "DataError" DOMException.

  4. If options.recurrentBias exists:

    1. If its rank is not 1, then throw a "DataError" DOMException.

    2. If options.recurrentBias.[[descriptor]].dimensions[0] is not 4 * hiddenSize, then throw a "DataError" DOMException.

  5. If options.peepholeWeight exists:

    1. If its rank is not 1, then throw a "DataError" DOMException.

    2. If options.peepholeWeight.[[descriptor]].dimensions[0] is not 3 * hiddenSize, then throw a "DataError" DOMException.

  6. If options.activations exists:

    1. If its size is not 3, then throw a TypeError.

  7. Let desc be a new MLOperandDescriptor.

  8. Set desc.dimensions to the list « batchSize, hiddenSize ».

  9. Set desc.dataType to input.[[descriptor]].dataType.

  10. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output0 be the result of creating an MLOperand given this and desc.

    2. Let output1 be the result of creating an MLOperand given this and desc.

    3. Let output be the list « output0, output1 ».

    4. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the LSTM cell operation, given weight, recurrentWeight, hiddenState, cellState, hiddenSize and options.

      2. Set output0.[[operator]] and output1.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output0.[[operand]] and output1.[[operand]] to outputImpl.

    5. Connect input.[[operand]] as input to opImpl.

    6. Connect output as output to opImpl.

  11. Return output.

The behavior of this operation can be generically emulated via other operations as shown below, when the weight layout is the default "iofg" layout, and the activation functions of the input/forget/output gate and the cell gate/the cell state’s filter for the output hidden state are of the operator types sigmoid and tanh respectively.
const zero = builder.constant(0);

// input gate (i)
let i = builder.sigmoid(
  builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ? builder.slice(options.peepholeWeight, [0], [hiddenSize]) : zero)
    ),
    builder.add(
      builder.add(
        (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
        (options.recurrentBias ? builder.slice(options.recurrentBias, [0], [hiddenSize]) : zero)
      ),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(weight, [0, 0], [hiddenSize, inputSize]))
        ),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(recurrentWeight, [0, 0], [hiddenSize, hiddenSize]))
        )
      )
    )
  )
);

// forget gate (f)
let f = builder.sigmoid(
  builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ? builder.slice(options.peepholeWeight, [2 * hiddenSize], [hiddenSize]) : zero)
    ),
    builder.add(
      builder.add(
        (options.bias ? builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) : zero),
        (options.recurrentBias ? builder.slice(options.recurrentBias, [2 * hiddenSize], [hiddenSize]) : zero)
      ),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))
        ),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(recurrentWeight, [2 * hiddenSize, 0], [hiddenSize, hiddenSize]))
        )
      )
    )
  )
);

// cell gate (g)
let g = builder.tanh(
  builder.add(
    builder.add(
      (options.bias ? builder.slice(options.bias, [3 * hiddenSize], [hiddenSize]) : zero),
      (options.recurrentBias ? builder.slice(options.recurrentBias, [3 * hiddenSize], [hiddenSize]) : zero)
    ),
    builder.add(
      builder.matmul(
        input,
        builder.transpose(builder.slice(weight, [3 * hiddenSize, 0], [hiddenSize, inputSize]))
      ),
      builder.matmul(
        hiddenState,
        builder.transpose(builder.slice(recurrentWeight, [3 * hiddenSize, 0], [hiddenSize, hiddenSize]))
      )
    )
  )
);

// output gate (o)
let o = builder.sigmoid(
  builder.add(
    builder.mul(
      cellState,
      (options.peepholeWeight ? builder.slice(options.peepholeWeight, [hiddenSize], [hiddenSize]) : zero)
    ),
    builder.add(
      builder.add(
        (options.bias ? builder.slice(options.bias, [hiddenSize], [hiddenSize]) : zero),
        (options.recurrentBias ? builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) : zero)
      ),
      builder.add(
        builder.matmul(
          input,
          builder.transpose(builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))
        ),
        builder.matmul(
          hiddenState,
          builder.transpose(builder.slice(recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize]))
        )
      )
    )
  )
);

// output cell state (ct)
let ct = builder.add(builder.mul(f, cellState), builder.mul(i, g));

// output hidden state (ht)
let ht = builder.mul(o, builder.tanh(ct));

return [ht, ct];

7.7.29. matmul

Compute the matrix product of two input tensors.
partial interface MLGraphBuilder {
  MLOperand matmul(MLOperand a, MLOperand b);
};
Arguments:

Returns: an MLOperand. The output tensor that contains the matrix product of two input tensors.

Computes the matrix product of two input tensors as follows:
To calculate matmul output sizes, given a and b run the following steps:
  1. Let shapeA be a.[[descriptor]].dimensions and sizeA the size of shapeA.

  2. Let shapeB be b.[[descriptor]].dimensions and sizeB the size of shapeB.

  3. If sizeA and sizeB is 1, return the list « 1 ».

  4. If sizeA is 1 and sizeB is not, then insert 1 in the front of shapeA to become [ 1 | shapeA ] and let sizeA be 2.

  5. If shapeA[0] is not equal to shapeB[sizeB - 2], then throw an "OperationError" DOMException.

  6. If sizeB is 1 and sizeA is not, then append 1 to shapeB to become [ shapeB | 1 ] and let sizeB be 2.

  7. If shapeA[sizeA - 1] is not equal to shapeB[0], then throw an "OperationError" DOMException.

  8. Let shape be an array whose size size is the maximum of sizeA and sizeB.

  9. For each index in the range 0 to size, exclusive:

    1. Set shape[index] to the maximum of shapeA[index] and shapeB[index].

  10. Return shape.

The matmul(a, b) method steps are:
  1. Let desc be a new MLOperandDescriptor.

  2. Set desc.dimensions to the result of calculating matmul output sizes given a and b.

  3. If that throws an error, re-throw the error.

  4. Set desc.dataType to a.[[descriptor]].dataType.

  5. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the matrix multiplication operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect a.[[operand]] and b.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  6. Return output.

7.7.30. pad

Inflate the tensor with constant or mirrored values on the edges.
enum MLPaddingMode {
  "constant",
  "edge",
  "reflection",
  "symmetric"
};

dictionary MLPadOptions {
  MLPaddingMode mode = "constant";
  float value = 0;
};

partial interface MLGraphBuilder {
  MLOperand pad(MLOperand input,
                sequence<unsigned long> beginningPadding,
                sequence<unsigned long> endingPadding,
                optional MLPadOptions options = {});
};

MLPadOptions has the following members:

mode, of type MLPaddingMode, defaulting to "constant"

An MLPaddingMode string. Specifies the different ways to pad the tensor. The default value is "constant".

value, of type float, defaulting to 0

A float. Specifies the padding value when mode is set to "constant". The default value is 0.

Arguments:

Returns: an MLOperand. The padded output tensor. Each dimension of the output tensor can be calculated as follow:

output size = beginning padding + input size + ending padding

To calculate padding output sizes, given input, beginningPadding and endingPadding, run the following steps:
  1. Let shape be a copy of input.[[descriptor]].dimensions.

  2. For index in the range 0 to the rank of shape, exclusive:

    1. Add to shape[index] the value of beginningPadding[index].

    2. Add to shape[index] the value of endingPadding[index].

  3. Return shape.

The pad(input, beginningPadding, endingPadding, options) method steps are:
  1. If the size of beginningPadding and endingPadding is not equal to the rank of input, then then throw a "TypeError".

  2. Let desc be a copy of input.[[descriptor]].

  3. Set desc.dimensions to the result of calculating padding output sizes given input, beginningPadding and endingPadding.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the padding operation, given beginningPadding, endingPadding and options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  5. Return output.

Examples for constant, edge, reflection and symmetric padding:
// input: [[1,2,3], [4,5,6]]
const input = builder.constant(
  { dataType: 'float32', dimensions: [2,3] }, new Float32Array([1,2,3,4,5,6]));

const beginningPadding = [1,2];
const endingPadding = [1,2];

// "constant" padded:
//    [[0,0,0,0,0,0,0],
//     [0,0,1,2,3,0,0],
//     [0,0,4,5,6,0,0],
//     [0,0,0,0,0,0,0]]
builder.pad(input, beginningPadding, endingPadding);

// "edge" padded:
//    [[1,1,1,2,3,3,3],
//     [1,1,1,2,3,3,3],
//     [4,4,4,5,6,6,6],
//     [4,4,4,5,6,6,6]]
builder.pad(input, beginningPadding, endingPadding, { mode: "edge" });

// "reflection" padded:
//    [[6,5,4,5,6,5,4],
//     [3,2,1,2,3,2,1],
//     [6,5,4,5,6,5,4],
//     [3,2,1,2,3,2,1]]
builder.pad(input, beginningPadding, endingPadding, { mode: "reflection" });

// "symmetric" padded:
//    [[2,1,1,2,3,3,2],
//     [2,1,1,2,3,3,2],
//     [5,4,4,5,6,6,5],
//     [5,4,4,5,6,6,5]]
builder.pad(input, beginningPadding, endingPadding, { mode: "symmetric" });

7.7.31. Pooling operations

Compute a pooling operation across all the elements within the moving window over the input tensor.
enum MLRoundingType {
  "floor",
  "ceil"
};

dictionary MLPool2dOptions {
  sequence<unsigned long> windowDimensions;
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  MLAutoPad autoPad = "explicit";
  MLInputOperandLayout layout = "nchw";
  MLRoundingType roundingType = "floor";
  sequence<unsigned long> outputSizes;
};

partial interface MLGraphBuilder {
  MLOperand averagePool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand l2Pool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand maxPool2d(MLOperand input, optional MLPool2dOptions options = {});
};

MLPool2dOptions has the following members:

windowDimensions, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [windowHeight, windowWidth]. Specifies the dimensions of the sliding window. The default value for the window dimensions are the height and width dimensions of the input shape.

padding, of type sequence<unsigned long>

A sequence of unsigned long of length 4: [beginningHeight, endingHeight, beginningWidth, endingWidth]. Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input. The default value is [0,0,0,0].

strides, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [strideHeight, strideWidth]. Specifies the stride of the sliding window for each spatial dimension of the convolution input. The default value is [1,1].

dilations, of type sequence<unsigned long>

A sequence of unsigned long of length 2: [dilationHeight, dilationWidth]. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel). The default value is [1,1].

autoPad, of type MLAutoPad, defaulting to "explicit"

An MLAutoPad string. Specifies the automatic input padding options. The default value is "explicit", which means that the values in the padding array should be used for input padding. When the option is set other than "explicit", the values in the padding array are ignored.

With the "same-upper" option, the padding values are automatically computed such that the additional ending padding of the spatial input dimensions would allow all of the input values in the corresponding dimension to be filtered.

The "same-lower" option is similar but padding is applied to the beginning padding of the spatial input dimensions instead of the ending one.

layout, of type MLInputOperandLayout, defaulting to "nchw"

An MLInputOperandLayout string. Specifies the layout format of the input and output tensor as follows:

  • "nchw"

    • input tensor: [batches, inputChannels, height, width]

    • output tensor: [batches, outputChannels, height, width]

  • "nhwc":

    • input tensor: [batches, height, width, inputChannels]

    • output tensor: [batches, height, width, outputChannels]

The default value is "nchw".
roundingType, of type MLRoundingType, defaulting to "floor"

An MLRoundingType string. Specifies the rounding function used to compute the output shape.

outputSizes, of type sequence<unsigned long>

A sequence of unsigned long of length 2. Specifies the sizes of the two spacial dimensions of the output tensor. When the output sizes are explicitly specified, the roundingType is ignored.

If not specified, the output sizes are automatically computed.

Arguments:

Returns: an MLOperand. The output 4-D tensor that contains the result of the reduction. The logical shape is interpreted according to the value of layout. More specifically, if the options.roundingType is "floor", the spatial dimensions of the output tensor can be calculated as follow:

output size = floor(1 + (input size - filter size + beginning padding + ending padding) / stride)

or if options.roundingType is "ceil":

output size = ceil(1 + (input size - filter size + beginning padding + ending padding) / stride)

A global pooling operation such as one for the max pooling operation is a variant of pooling where the window dimensions is the spatial dimensions (last two dimensions) of the input shape, as follow.
// 'global' max pooling
builder.maxPool2d(input);
To create pooling operation given op, MLOperand input and MLPool2dOptions options, run the following steps:
  1. Assert: op is one of "averagePool2d", "l2Pool2d", "maxPool2d".

  2. If the size of input.[[descriptor]].dimensions is not 4, then throw a "DataError" DOMException.

  3. If options.windowDimensions exists and its size is not 2, then throw a "DataError" DOMException.

  4. Otherwise, set options.windowDimensions to the height and width dimensions of the shape of input.

  5. If options.outputSizes exists, or if options.padding does not exist, set options.padding to the list « 0, 0, 0, 0 ».

  6. If the size of options.padding is not 4, then throw a "DataError" DOMException.

  7. If options.strides does not exist, set options.strides to the list « 1, 1 ».

  8. If the size of options.strides is not 2, then throw a "DataError" DOMException.

  9. If any value in options.strides is not greater than 0, then throw a "DataError" DOMException.

  10. If options.outputSizes exists:

    1. If the size of options.outputSizes is not 2, then throw a TypeError.

    2. If the elements of options.outputSizes are not smaller than the elements at the same dimension (index) for options.strides, then throw a "DataError" DOMException.

  11. If options.dilations does not exist, set options.dilations to the list « 1, 1 ».

  12. If the size of options.dilations is not 2, then throw a "DataError" DOMException.

  13. If any value in options.dilations is not greater than 0, then throw a "DataError" DOMException.

  14. If options.autoPad is not "explicit", set options.padding to the list « 0, 0, 0, 0 ».

  15. Let desc be a copy of input.[[descriptor]].

  16. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Make a request to the underlying platform to:

      1. Calculate the output dimensions given input and options. Set desc.dimensions to that.

      2. Let output be the result of creating an MLOperand given this and desc.

      3. Let opImpl be platform operator for the op pooling operation, given options.

      4. Set output.[[operator]] to opImpl.

      5. Create an platform operand outputImpl to represent the output, given output and opImpl.

      6. Set output.[[operand]] to outputImpl.

    2. Connect input.[[operand]] as input to opImpl.

    3. Connect output.[[operand]] as output to opImpl.

  17. Return output.

The following pooling algorithms are supported.
The averagePool2d(input, options) method steps are:
  1. Let output be the result of running the create pooling operation given "averagePool2d", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The l2Pool2d(input, options) method steps are:
  1. Let output be the result of running the create pooling operation given "l2Pool2d", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The maxPool2d(input, options) method steps are:
  1. Let output be the result of running the create pooling operation given "maxPool2d", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.31.1. averagePool2d
Calculate the average value for patches of a feature map, and use it to create a pooled feature map. See § 7.7.31 Pooling operations for more detail.
7.7.31.2. l2Pool2d
Apply the L2 norm function to a region of the input feature map. The L2 norm is the square root of the sum of the squares of its elements. See § 7.7.31 Pooling operations for more detail.
7.7.31.3. maxPool2d
Calculate the maximum value for patches of a feature map, and use it to create a pooled feature map. See § 7.7.31 Pooling operations for more detail.

7.7.32. prelu

Calculate the parametric version of rectified linear function (Parametric ReLU) on the input tensor element-wise. Parametric ReLU is a type of leaky ReLU that, instead of having a scalar slope like 0.01, making the slope (coefficient of leakage) into a parameter that is learned during the model training phase of this operation. The calculation follows the expression max(0, x) + slope ∗ min(0, x).
partial interface MLGraphBuilder {
  MLOperand prelu(MLOperand input, MLOperand slope);
};
Arguments:

Returns:

The prelu(input, slope) method steps are:
  1. Let descriptor be a new MLOperandDescriptor.

  2. Set descriptor.dataType to input.[[descriptor]].dataType.

  3. Set descriptor.dimensions to the result of running the broadcast-shapes steps given input.[[descriptor]].dimensions and slope.[[descriptor]].dimensions.

    1. If that throws an error, re-throw the error.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and descriptor.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the PreLU operation, given slope.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  5. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.add(builder.max(builder.constant(0), x),
                   builder.mul(slope, builder.min(builder.constant(0), x)));

7.7.33. Reduction operations

Reduce the input tensor along all dimensions, or along the axes specified in the axes array parameter. For each specified axis, the dimension with that index is reduced, i.e. the resulting tensor will not contain it, unless the keepDimensions option is specified. The values of the resulting tensor are calculated using the specified reduction function that takes as parameters all the values across the reduced dimension.
dictionary MLReduceOptions {
  sequence<unsigned long> axes = null;
  boolean keepDimensions = false;
};

partial interface MLGraphBuilder {
  MLOperand reduceL1(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceL2(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSumExp(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMax(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMean(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMin(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceProduct(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSumSquare(MLOperand input, optional MLReduceOptions options = {});
};

MLReduceOptions has the following members:

axes, of type sequence<unsigned long>, defaulting to null

A sequence of unsigned long. The dimensions to reduce. The values in the sequence must be in the range [0, N-1] where N is the rank of the input tensor. If not present, all dimensions are reduced.

keepDimensions, of type boolean, defaulting to false

A boolean. If true, retains reduced dimensions with size 1. The default value is false.

Arguments:

Returns: an MLOperand. The reduced output tensor.

Reduction types:
To create reduce operation given op, MLOperand input and MLReduceOptions options, run the following steps:
  1. Assert: op is one of "reduceL1", "reduceL2", "reduceLogSum", "reduceLogSumExp", "reduceMax", "reduceMean", "reduceMin", "reduceProduct", "reduceSum", "reduceSumSquare".

  2. If options.axes exists, if any of its elements is not in the range 0 to the rank of input, exclusive, then throw a "DataError" DOMException.

  3. Let outputShape be the result of invoking the underlying implementation for calculating reduction output dimensions, given options.

  4. Let desc be a new MLOperandDescriptor.

  5. Set desc.dataType to input.[[descriptor]].dataType.

  6. Set desc.dimensions to outputShape.

  7. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the op reduce operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  8. Return output.

The following reduce algorithms are supported.
The reduceL1(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceL1", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceL2(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceL2", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceLogSum(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceLogSum", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceLogSumExp(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceLogSumExp", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceMax(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceMax", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceMean(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceMean", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceMin(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceMin", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceProduct(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceProduct", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceSum(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceSum", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

The reduceSumSquare(input, options) method steps are:
  1. Let output be the result of running the create reduce operation given "reduceSumSquare", input and options.

    1. If that throws an error, then re-throw the error.

  2. Return output.

7.7.34. relu

Compute the rectified linear function of the input tensor.
partial interface MLGraphBuilder {
  MLOperand relu(MLOperand input);
  MLActivation relu();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.max(builder.constant(0), x);
7.7.34.1. relu(input)
Arguments:

Returns:

The relu(input) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the ReLU operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.34.2. relu()
Arguments:

Returns:

The relu() method steps are:
  1. Let op be the result of creating an MLActivation given this and "relu".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.35. resample2d

Resample the tensor values from the source to the destination spatial dimensions according to the scaling factors.
enum MLInterpolationMode {
  "nearest-neighbor",
  "linear"
};

dictionary MLResample2dOptions {
  MLInterpolationMode mode = "nearest-neighbor";
  sequence<float> scales;
  sequence<unsigned long> sizes;
  sequence<unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand resample2d(MLOperand input, optional MLResample2dOptions options = {});
};
Arguments:

Returns: an MLOperand. The output 4-D tensor.

MLResample2dOptions has the following members:

mode, of type MLInterpolationMode, defaulting to "nearest-neighbor"

An MLInterpolationMode string. Specifies the interpolation algorithm used to fill the output tensor values. The default value is "nearest-neighbor", standing for Nearest Neighbor interpolation.

scales, of type sequence<float>

A sequence of float of length 2. Specifies the scaling factor in each spatial dimensions of the input: [scaleHeight, scaleWidth]. The default value is [1.0, 1.0].

sizes, of type sequence<unsigned long>

A sequence of unsigned long of length 2. Specifies the target sizes for each spatial dimensions of the input: [sizeHeight, sizeWidth]. When the target sizes are specified, the scales argument is ignored, since the scaling factor values are derived from the target sizes of each spatial dimension of the input.

axes, of type sequence<unsigned long>

A sequence of unsigned long of length 2. Specifies the two consecutive dimensions of the input tensor to which the interpolation algorithm applies. The valid values in the sequence are [0, 1], [1, 2] or [2, 3]. The default value is [2, 3].

To check resample options given options, run the following steps:
  1. If options.scales does not exist, set it to to the list « 1.0, 1.0 ».

  2. Otherwise, if any of its values is not greater than 0, or if its size is not 2, return false.

  3. If options.sizes exists, and if its size is not 2, or if any of its values is not greater than 0, return false.

  4. If options.axes does not exists, set it to the list « 2, 3 ».

  5. Otherwise, if its value is not one of « 0, 1», « 1, 2», « 2, 3 », return false.

  6. Return true.

To calculate resample output sizes given MLOperand input and MLResample2dOptions options, run the following steps:
  1. Let desc be a new MLOperandDescriptor initialized to input.[[descriptor]].

  2. For index in the range 0 to the size of options.axes, exclusive:

    1. If options.sizes exists, set desc.[[descriptor]].dimensions[options.axes[index]] to options.sizes[index] and return desc.

    2. Otherwise, set desc.[[descriptor]].dimensions[options.axes[index]] to input.[[descriptor]].dimensions[index] multiplied by options.scales.

  3. Return desc.

The resample2d(input, options) method steps are:
  1. If the size of input.[[descriptor]].dimensions is not 4, then throw a "DataError" DOMException.

  2. If checking resample options given options returns false, then throw a "DataError" DOMException.

  3. Let desc be the result of calculating resample output sizes given input and options.

  4. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the resample 2D operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  5. Return output.

7.7.36. reshape

Alter the shape of a tensor to a new shape. Reshape does not copy or change the content of the tensor. It just changes the tensor’s logical dimensions for the subsequent operations.
partial interface MLGraphBuilder {
  MLOperand reshape(MLOperand input, sequence<unsigned long> newShape);
};
Arguments:

Returns: an MLOperand. The output tensor. The values of the output tensor are the same as values of the input tensor. The shape of the output tensor is specified by the newShape argument.

The reshape(input, newShape) method steps are:
  1. Let outputShape be an empty array of unsigned long.

  2. If newShape is a scalar number, set outputShape to the list « 1 ».

  3. Otherwise, if newShape is an array of unsigned long:

    1. If the size of newShape is 0, set outputShape to the list « 1 » (reshaping to scalar).

    2. If any value in newShape is 0, then throw a "DataError" DOMException.

    3. Let inputElementCount be the product of all elements in input.[[descriptor]].dimensions.

    4. If product of all values in newShape is not equal to inputElementCount, then throw a "DataError" DOMException.

  4. Let desc be a copy of input.[[descriptor]].

  5. Set desc.dimensions to newShape.

  6. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and desc.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the reshape operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  7. Return output.

Many shape-related operations such as squeeze, unsqueeze, and flatten can be generically implemented using the reshape}} operation as follows:
// Returns a tensor with all specified dimensions of input of size 1 removed.
function squeeze(input, axes) {
  if (!axes) axes = [];
  if (!axes.length)
    input.shape().forEach((item, i) => { axes.push(i); });
  shape = Array.from(input.shape());
  for (let axis in axes.sort().reverse())
    if (axis < shape.length && shape[axis] == 1)
      shape.splice(axis, 1);
  return builder.reshape(input, shape);
}

// Returns a new tensor with a dimension of size one inserted at the specified position.
function unsqueeze(input, axes) {
  shape = Array.from(input.shape());
  for(let axis in axes.sort())
    shape.splice(axis, 0, 1);
  return builder.reshape(input, shape);
}

// Flattens input by reshaping it into a one-dimensional tensor. 
function flatten(input, axis) {
  if (axis > input.shape().length) return input;
  let before = axis.slice(0, axis).reduce((a, b) => { a * b; });
  let after = axis.slice(axis, input.shape().length).reduce((a, b) => { a * b; });
  return builder.reshape(input, [before, after]);
}

7.7.37. sigmoid

Compute the sigmoid function of the input tensor. The calculation follows the expression 1 / (exp(-x) + 1).
partial interface MLGraphBuilder {
  MLOperand sigmoid(MLOperand input);
  MLActivation sigmoid();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.div(
          builder.constant(1),
          builder.add(
            builder.exp(builder.neg(x)),
            builder.constant(1)));
7.7.37.1. sigmoid(input)
Arguments:

Returns:

The sigmoid(input) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the sigmoid operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.37.2. sigmoid()
Arguments:

Returns:

The sigmoid() method steps are:
  1. Let op be the result of creating an MLActivation given this and "sigmoid".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.38. slice

Produce a slice of the input tensor.
partial interface MLGraphBuilder {
  MLOperand slice(MLOperand input, sequence<unsigned long> starts, sequence<unsigned long> sizes);
};
Arguments:

Returns: an MLOperand. The output tensor of the same rank as the input tensor with tensor values stripped to the specified starting and ending indices in each dimension.

The slice(input, starts, sizes) method steps are:
  1. If sizes.size is 0, then throw a TypeError.

  2. If the size of starts and sizes is not equal to the rank of input, then throw a TypeError.

  3. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the slice operation, given starts and sizes.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  4. Return output.

7.7.39. softmax

Compute the softmax values of the 2-D input tensor along axis 1.
partial interface MLGraphBuilder {
  MLOperand softmax(MLOperand input);
  MLActivation softmax();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
// This sample deploys a well-known implementation trick [1] to compute the
// exponentials of the distances to the max value, instead of the exponentials
// of the input values itself, in order to increase the numerical stability of
// the result.
// [1]: https://cs231n.github.io/linear-classify/#softmax
const max_x = builder.reduceMax(x, { axes: [1], keepDimensions: true });
const exp_x = builder.exp(builder.sub(x, max_x));
return builder.div(exp_x, builder.reduceSum(exp_x, { axes: [1], keepDimensions: true }));
7.7.39.1. softmax(input)
Arguments:

Returns:

The softmax(input) method steps are:
  1. If the size of input.[[descriptor]].dimensions is not 2, then throw a "DataError" DOMException.

  2. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the softmax operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  3. Return output.

7.7.39.2. softmax()
Arguments:

Returns:

The softmax() method steps are:
  1. Let op be the result of creating an MLActivation given this and "softmax".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.40. softplus

Compute the softplus function of the input tensor. The calculation follows the expression ln(1 + exp(steepness * x)) / steepness.
dictionary MLSoftplusOptions {
  float steepness = 1;
};

partial interface MLGraphBuilder {
  MLOperand softplus(MLOperand input, optional MLSoftplusOptions options = {});
  MLActivation softplus(optional MLSoftplusOptions options = {});
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.div(
          builder.log(
            builder.add(
              builder.exp(builder.mul(x, builder.constant(options.steepness))),
              builder.constant(1))),
          builder.constant(options.steepness));

MLSoftplusOptions has the following members:

steepness, of type float, defaulting to 1

A float scalar parameter. The default value is 1.

7.7.40.1. softplus(input, options)
Arguments:

Returns:

The softplus(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the softplus operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.40.2. softplus(options)
Arguments:

Returns:

The softplus(options) method steps are:
  1. Let op be the result of creating an MLActivation given this, "softplus" and options.

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.41. softsign

Compute the softsign function of the input tensor. The calculation follows the expression x / (1 + |x|).
partial interface MLGraphBuilder {
  MLOperand softsign(MLOperand input);
  MLActivation softsign();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.div(x, builder.add(builder.constant(1), builder.abs(x)));
7.7.41.1. softsign(input)
Arguments:

Returns:

The softsign(input) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the softsign operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.41.2. softsign()
Arguments:

Returns:

The softsign() method steps are:
  1. Let op be the result of creating an MLActivation given this and "softsign".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.42. split

Split the input tensor into a number of sub tensors along the given axis.
dictionary MLSplitOptions {
  unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> split(MLOperand input,
                          (unsigned long or sequence<unsigned long>) splits,
                          optional MLSplitOptions options = {});
};
Arguments:

Returns: a sequence of MLOperand. The splitted output tensors. If splits is an unsigned long, the size of the output sequence equals to splits. The shape of each output tensor is the same as input except the dimension size of axis equals to the quotient of dividing the dimension size of input along axis by splits. If splits is a sequence of unsigned long, the size of the output sequence equals to the size of splits. The shape of the i-th output tensor is the same as as input except along axis where the dimension size is splits[i].

MLSplitOptions has the following members:

axis, of type unsigned long, defaulting to 0

An unsigned long scalar. The dimension along which to split. Its value must be in the range [0, N-1] where N is the rank of the input tensor. The default value is 0.

The split(input, splits, options) method steps are:
  1. If splits is an unsigned long, and input.dimensions[options.axis] % splits is not 0, then throw a TypeError.

  2. If splits is a sequence of unsigned long, and the sum of its elements is not equal to input.dimensions[options.axis], then throw a TypeError.

  3. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the split operation, given splits and options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  4. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
// This sample shows the case that the splits parameter is an array.
const outputs = [];
let starts = Array(input_rank).fill(0);
let sizes = input_shape;
let start = 0;
for (const size of splits) {
  starts[options.axis] = start;
  sizes[options.axis] = size;
  outputs.push(builder.slice(input, starts, sizes));
  start += size;
}
return outputs;

7.7.43. tanh

Compute the hyperbolic tangent function of the input tensor. The calculation follows the expression (exp(2 * x) - 1) / (exp(2 * x) + 1).
partial interface MLGraphBuilder {
  MLOperand tanh(MLOperand input);
  MLActivation tanh();
};
The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
return builder.div(
          builder.sub(builder.exp(builder.mul(builder.constant(2), x)), builder.constant(1)),
          builder.add(builder.exp(builder.mul(builder.constant(2), x)), builder.constant(1)));
7.7.43.1. tanh(input)
Arguments:

Returns:

The tanh(input) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the hyperbolic tangent operation.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

7.7.43.2. tanh()
Arguments:

Returns:

The tanh() method steps are:
  1. Let op be the result of creating an MLActivation given this and "tanh".

    1. If that throws an error, re-throw the error.

  2. Return op.

7.7.44. transpose

Permute the dimensions of the input tensor according to the permutation argument.
dictionary MLTransposeOptions {
  sequence<unsigned long> permutation;
};

partial interface MLGraphBuilder {
  MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {});
};

MLTransposeOptions has the following members:

permutation, of type sequence<unsigned long>

A sequence of unsigned long values. Specifies the values used to permute the output shape. The default value is [N-1, ..., 0], where N is the rank of the input tensor, e.g. [2,1,0] for a 3-D tensor. These default values cause the output to become a transposed tensor of the input. When specified, the number of values in the sequence must be the same as the rank of the input tensor, and the values in the sequence must be within the range from 0 to N-1 with no two or more same values found in the sequence.

Arguments:

Returns: an MLOperand. The permuted or transposed N-D tensor.

The transpose(input, options) method steps are:
  1. If options.permutation does not exist, let options.permutation be the reversed sequence of all indices for input.[[descriptor]].dimensions.

  2. Otherwise if options.permutation exists:

    1. If the rank of options.permutation is not the same as the rank of input.[[descriptor]].dimensions, then throw a TypeError.

    2. If the values in options.permutation are not in the range 0 and the rank of input.[[descriptor]].dimensions exclusive, then throw a TypeError.

    3. If the values in options.permutation contain duplicate value, then throw a TypeError.

  3. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the transpose operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  4. Return output.

7.7.45. triangular

Given a 2-D tensor (matrix), return a 2-D tensor containing either the upper or lower triangular part of the input tensor.
dictionary MLTriangularOptions {
  boolean upper = true;
  long diagonal = 0;
};

partial interface MLGraphBuilder {
  MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {});
};

MLTriangularOptions has the following members:

upper, of type boolean, defaulting to true

A boolean value. Indicate whether the output the upper or the lower part of the input matrix is retained. If not set, it is assumed to be true, indicating that the upper part is retained.

diagonal, of type long, defaulting to 0

A long value. Specify how many diagonals above or below the main diagonals of the input matrix are retained or excluded. If not set, this value is assumed to be 0, which means no diagonals other than the main diagonals are affected.

Arguments:

Returns: an MLOperand. The output 2-D tensor representing a triangular matrix.

The triangular(input, options) method steps are:
  1. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of copying an MLOperand given input.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the triangular operation, given options.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect input.[[operand]] as input to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  2. Return output.

Examples of how triangular works in different diagonal settings.
// input:
//   [[7, 1, 2],
//    [9, 4, 8],
//    [2, 6, 3]]
const input = builder.constant(
{ dimensions: [3,3] }, new Float32Array([7,1,2,9,4,8,2,6,3]));

// upper triangular matrix:
//   [[7, 1, 2], 
//    [0, 4, 8],
//    [0, 0, 3]]
const upper = builder.triangular(input);

// upper triangular matrix with one additional set of diagonals excluded:
//   [[0, 1, 2], 
//    [0, 0, 8],
//    [0, 0, 0]]
const upperPositive = builder.triangular(input, { diagonal: 1 });

// upper triangular matrix with one additional set of diagonals retained:
//   [[7, 1, 2], 
//    [9, 4, 8],
//    [0, 6, 3]]
const upperNegative = builder.triangular(input, { diagonal: -1 });

// lower triangular matrix:
//   [[7, 0, 0],
//    [9, 4, 0],
//    [2, 6, 3]]
const lower = builder.triangular(input, { upper: false });

// lower triangular matrix with one additional set of diagonals retained:
//   [[7, 1, 0],
//    [9, 4, 8],
//    [2, 6, 3]]
const lowerPositive = builder.triangular(input, { upper: false, diagonal: 1 });

// lower triangular matrix with one additional set of diagonals excluded:
//   [[0, 0, 0],
//    [9, 0, 0],
//    [2, 6, 0]]
const lowerNegative = builder.triangular(input, { upper: false, diagonal: -1 });

7.7.46. where

Select the values from the input or the other tensor depending on the corresponding boolean values of the condition tensor. The condition tensor is often the output of one of the element-wise logical operations.

The input tensors will be broadcasted according to [numpy-broadcasting-rule] to the final output shape. The rank of the output tensor is the maximum rank of the input tensors. For each dimension of the output tensor, its size is the maximum size along that dimension of the input tensors.

partial interface MLGraphBuilder {
  MLOperand where(MLOperand condition, MLOperand input, MLOperand other);
};
Arguments:

Returns: an MLOperand. The output tensor that contains the values selected element-wise from either the input or the other tensor.

The where(condition, input, other) method steps are:
  1. If condition.[[descriptor]].dataType is not equal to "uint8", then throw a "DataError" DOMException.

  2. If input.[[descriptor]].dataType is not equal to other.[[descriptor]].dataType, then throw a "DataError" DOMException.

  3. Let descriptor be a new MLOperandDescriptor.

  4. Set descriptor.dataType to input.[[descriptor]].dataType.

  5. Set descriptor.dimensions to the result of running the broadcast-shapes steps given input.[[descriptor]].dimensions and other.[[descriptor]].dimensions.

    1. If that throws an error, re-throw the error.

  6. If condition is not unidirectionally broadcastable to descriptor.dimensions according to the [numpy-broadcasting-rule], then throw a "DataError" DOMException.

  7. If any of the following sub-steps fail, throw an "OperationError" DOMException.

    1. Let output be the result of creating an MLOperand given this and descriptor.

    2. Make a request to the underlying platform to:

      1. Let opImpl be platform operator for the where operation, given condition, input and other.

      2. Set output.[[operator]] to opImpl.

      3. Create an platform operand outputImpl to represent the output, given output and opImpl.

      4. Set output.[[operand]] to outputImpl.

    3. Connect condition.[[operand]], input and other.[[operand]] as inputs to opImpl.

    4. Connect output.[[operand]] as output to opImpl.

  8. Return output.

The behavior of this operation can be generically emulated from the usage of other operations as follow. However, user agents typically have a more efficient implementation for it, therefore its usage is encouraged from the performance standpoint.
const c = builder.clamp(condition, {'minValue': 0, 'maxValue': 1});
builder.add(
  builder.mul(
    input,
    builder.cast(c, input.dataType())),
  builder.mul(
    other,
    builder.cast(builder.not(c), other.dataType())));

7.8. MLOperand interface

An MLOperand represents an intermediary graph being constructed as a result of compositing parts of an operation into a fully composed operation.

For instance, an MLOperand may represent a constant feeding to an operation or the result from combining multiple constants together into an operation. See also § 6 Programming Model.

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLOperand {};
MLOperand has the following internal slots:
[[builder]] of type MLGraphBuilder

The MLOperand's associated builder object.

[[descriptor]] of type MLOperandDescriptor

The MLOperand's descriptor.

[[name]] of type string

The MLOperand's name (only for input operands).

[[operand]] of type platform operand

Reference to MLOperand's corresponding platform operand.

[[operator]] of type platform operator

Reference to MLOperand's corresponding platform operator.

To get the rank of an MLOperand operand, run the following steps:
  1. Return the size of operand.[[descriptor]].dimensions.

Since the [[builder]] object is bound by the MLGraphBuilder() constructor to an MLContext object, an MLOperand is also always bound to the same MLContext object.

7.8.1. Creating MLOperand

The MLOperand objects are created by the methods of MLGraphBuilder, internally using the following algorithms.
To create an MLOperand given MLGraphBuilder builder and MLOperandDescriptor desc, run the following steps:
  1. Let operand be a new MLOperand.

  2. Set operand.[[builder]] to builder.

  3. Set operand.[[descriptor]] to desc.

  4. Return operand.

To copy an MLOperand given MLOperand operand, run the following steps:
  1. Let result be a new MLOperand.

  2. Set result.[[builder]] to operand.[[builder]].

  3. Set result.[[descriptor]] to operand.[[descriptor]].

  4. If operand.[[name]] exists, then set result.[[name]] to operand.[[name]].

  5. Return result.

To check dimensions given dimensions and type, run the following steps:
  1. If the size of dimensions is 0, return false.

  2. If the size of dimensions is too large to be supported by the implementation, return false.

  3. If any element of dimensions is not a positive number, or it is too large to be supported by the implementation given type, return false.

  4. Return true.

To validate MLOperand given MLOperand operand and MLGraphBuilder builder, run the following steps:
  1. If builder is not equal to operand.[[builder]], return false.

  2. Let desc be operand.[[descriptor]].

  3. If desc.dimensions exists and checking dimensions given desc.dimensions and desc.dataType returns false, then return false.

  4. Return true.

7.8.2. dataType

Return a data type of the MLOperand.
partial interface MLOperand {
  MLOperandDataType dataType();
};
Returns: an MLOperandDataType. The data type of the operand.
The dataType() method steps are:

7.8.3. shape

Return a shape of the MLOperand.
partial interface MLOperand {
  sequence<unsigned long> shape();
};
Returns: a sequence of unsigned long. The shape of the operand.
The shape() method steps are:

7.9. MLOperandDescriptor dictionary

enum MLInputOperandLayout {
  "nchw",
  "nhwc"
};

enum MLOperandDataType {
  "float32",
  "float16",
  "int32",
  "uint32",
  "int64",
  "uint64",
  "int8",
  "uint8"
};

dictionary MLOperandDescriptor {
  // The operand type.
  required MLOperandDataType dataType;

  // The dimensions field is only required for tensor operands.
  sequence<unsigned long> dimensions;
};
The byte length of an MLOperandDescriptor desc is the value returned by the following steps:
  1. Let elementLength be 1.

  2. For each dimension of desc.dimensions:

    1. Set elementLength to elementLength × dimension.

  3. Let elementSize be the element size of one of the ArrayBufferView types that matches desc.dataType according to this table.

  4. Return elementLength × elementSize.

8. Examples

The following code gets the MLContext object.
const context = await navigator.ml.createContext({powerPreference: 'low-power'});
Given the following build graph:
constant1 ---+
            +--- Add ---> intermediateOutput1 ---+
input1    ---+                                    |
                                                +--- Mul---> output
constant2 ---+                                    |
            +--- Add ---> intermediateOutput2 ---+
input2    ---+
The following code implements the graph:
// Use tensors in 4 dimensions.
const TENSOR_DIMS = [1, 2, 2, 2];
const TENSOR_SIZE = 8;

const builder = new MLGraphBuilder(context);

// Create MLOperandDescriptor object.
const desc = {dataType: 'float32', dimensions: TENSOR_DIMS};

// constant1 is a constant MLOperand with the value 0.5.
const constantBuffer1 = new Float32Array(TENSOR_SIZE).fill(0.5);
const constant1 = builder.constant(desc, constantBuffer1);

// input1 is one of the input MLOperands. Its value will be set before execution.
const input1 = builder.input('input1', desc);

// constant2 is another constant MLOperand with the value 0.5.
const constantBuffer2 = new Float32Array(TENSOR_SIZE).fill(0.5);
const constant2 = builder.constant(desc, constantBuffer2);

// input2 is another input MLOperand. Its value will be set before execution.
const input2 = builder.input('input2', desc);

// intermediateOutput1 is the output of the first Add operation.
const intermediateOutput1 = builder.add(constant1, input1);

// intermediateOutput2 is the output of the second Add operation.
const intermediateOutput2 = builder.add(constant2, input2);

// output is the output MLOperand of the Mul operation.
const output = builder.mul(intermediateOutput1, intermediateOutput2);
Compile the graph up to the output operand.
// Compile the constructed graph.
const graph = await builder.build({'output': output});
The following code executes the compiled graph.
// Setup the input buffers with value 1.
const inputBuffer1 = new Float32Array(TENSOR_SIZE).fill(1);
const inputBuffer2 = new Float32Array(TENSOR_SIZE).fill(1);
const outputBuffer = new Float32Array(TENSOR_SIZE);

// Execute the compiled graph with the specified inputs.
const inputs = {
'input1': inputBuffer1,
'input2': inputBuffer2,
};
const outputs = {'output': outputBuffer};
const result = await context.compute(graph, inputs, outputs);

console.log('Output value: ' + result.outputs.output);
// Output value: 2.25,2.25,2.25,2.25,2.25,2.25,2.25,2.25

9. Appendices

9.1. MLOperandDataType and ArrayBufferView compatibility

MLOperandDataType ArrayBufferView
float32 Float32Array
float16 Float16Array
int32 Int32Array
uint32 Uint32Array
int8 Int8Array
uint8 Uint8Array

Float16Array is at ECMA Stage 3 signaling its design is finished. Implementers wanting to enable this type ahead native implementations can emulate the type by passing raw bits via Uint16Array. [Issue webnn#373]

10. Acknowledgements

This specification follows the concepts of the Android Neural Networks API C API.

Thanks to Tomoyuki Shimizu, Ningxin Hu, Zhiqiang Yu and Belem Zhang for the use cases.

Thanks to Nikhil Thorat, Daniel Smilkov, Ganesan Ramalingam, Rafael Cintron and Benjamin Poulain for their contributions to the API specification.

Thanks to Sangwhan Moon and the W3C Technical Architecture Group for review of this specification for web architecture fit, design consistency and developer ergonomics.

Thanks to Zoltan Kis for adding algorithms and making navigating this specification a delightful experience. Thanks to Joshua Bell for aligning the specification with modern editorial conventions. Thanks to Ningxin Hu, Lisha Guo, Shiyi Zou, Mingming Xu, Junwei Fu, Bruce Dai and Bin Miao for careful review and comments.

Thanks to W3C Privacy Interest Group for privacy and security review and feedback.

Thanks to Alex Gough and the Chrome Security team for security review and questions.

Thanks to Michal Karzynski for sharing practical guidelines and learnings from ONNX.

Thanks to Kaustubha Govind and Chrome privacy reviewers for feedback and privacy considerations.

Thanks to Jiewei Qian for Chromium implementation review and feedback.

Thanks to Dwayne Robinson for his work investigating and providing recommendation for transformer support, and for providing reviews of operator conformance and WPT implementation.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 18 December 2023. WD. URL: https://www.w3.org/TR/css-values-4/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[NUMPY-BROADCASTING-RULE]
The SciPy community. General Broadcasting Rules of NumPy. July 2019. URL: https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 18 December 2023. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBGPU]
Kai Ninomiya; Brandon Jones; Jim Blandy. WebGPU. 25 January 2024. WD. URL: https://www.w3.org/TR/webgpu/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[Batch-Normalization]
Sergey Ioffe; Christian Szegedy. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. March 2015. URL: https://arxiv.org/abs/1502.03167
[ContextualLoss]
Roey Mechrez; Itamar Talmi; Lihi Zelnik-Manor. The Contextual Loss for Image Transformation with Non-Aligned Data. July 2018. URL: https://arxiv.org/abs/1803.02077
[DeepLabv3+]
Liang-Chieh Chen; et al. Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation. August 2018. URL: https://arxiv.org/abs/1802.02611
[DeepMoji]
Bjarke Felbo; et al. Using millions of emoji occurrences to learn any-domain representations for detecting sentiment, emotion and sarcasm. October 2017. URL: https://arxiv.org/abs/1708.00524
[ELU]
Djork-Arné Clevert; Thomas Unterthiner; Sepp Hochreiter. Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs). February 2016. URL: https://arxiv.org/abs/1511.07289
[Error-Function]
Larry C. Andrews. Special functions of mathematics for engineers. 1998. URL: https://books.google.com/books?id=2CAqsF-RebgC&pg=PA110
[FaceForensics++]
Andreas Rössler; et al. FaceForensics++. January 2019. URL: https://github.com/ondyari/FaceForensics
[FaceNet]
Florian Schroff; Dmitry Kalenichenko; James Philbin. FaceNet: A Unified Embedding for Face Recognition and Clustering. June 2015. URL: https://arxiv.org/abs/1503.03832
[FAN]
Adrian Bulat; Georgios Tzimiropoulos. How far are we from solving the 2D & 3D Face Alignment problem? (and a dataset of 230,000 3D facial landmarks). September 2017. URL: https://arxiv.org/abs/1703.07332
[GNMT]
Minh-Thang Luong; Eugene Brevdo; Rui Zhao. Neural Machine Translation (seq2seq) Tutorial. May 2017. URL: https://github.com/tensorflow/nmt
[GPT2]
Alec Radford; et al. Language Models are Unsupervised Multitask Learners. February 2019. URL: https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf
[GRU]
Kyunghyun Cho; et al. Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation. September 2014. URL: https://arxiv.org/pdf/1406.1078.pdf
[HR-TIME-3]
Yoav Weiss. High Resolution Time. 19 July 2023. WD. URL: https://www.w3.org/TR/hr-time-3/
[IM2TXT]
Oriol Vinyals; et al. Show and Tell: Lessons learned from the 2015 MSCOCO Image Captioning Challenge. September 2016. URL: https://arxiv.org/abs/1609.06647
[Instance-Normalization]
Dmitry Ulyanov; Andrea Vedaldi; Victor Lempitsky. Instance Normalization: The Missing Ingredient for Fast Stylization. July 2016. URL: https://arxiv.org/abs/1607.08022
[Layer-Normalization]
Jimmy Lei Ba; Jamie Ryan Kiros; Geoffrey E. Hinton. Layer Normalization. July 2016. URL: https://arxiv.org/abs/1607.06450
[LDM]
Robin Rombach; et al. High-Resolution Image Synthesis with Latent Diffusion Models. April 2022. URL: https://arxiv.org/abs/2112.10752
[LeakyReLU]
Andrew L. Maas; Awni Y. Hannun; Andrew Y. Ng. Rectifier Nonlinearities Improve Neural Network Acoustic Models. June 2013. URL: https://pdfs.semanticscholar.org/367f/2c63a6f6a10b3b64b8729d601e69337ee3cc.pdf
[LLAMA-2-7B]
Hugo Touvron; et al. Llama 2: Open Foundation and Fine-Tuned Chat Models. July 2023. URL: https://arxiv.org/abs/2307.09288
[LSTM]
Sepp Hochreiter; Jürgen Schmidhuber. Long Short-Term Memory. November 1997. URL: https://doi.org/10.1162/neco.1997.9.8.1735
[m2m100_418M]
Angela Fan; et al. Beyond English-Centric Multilingual Machine Translation. October 2020. URL: https://arxiv.org/abs/2010.11125
[MaskR-CNN]
Kaiming He; et al. Mask R-CNN. January 2018. URL: https://arxiv.org/abs/1703.06870
[MobileNetV3]
Andrew Howard; et al. Searching for MobileNetV3. November 2019. URL: https://arxiv.org/pdf/1905.02244
[MODELS]
Machine Learning for the Web Community Group. The first-wave models. 2020. URL: https://github.com/webmachinelearning/webnn/blob/master/op_compatibility/first_wave_models.md
[OpenNMT]
Guillaume Klein; et al. OpenNMT: Open-Source Toolkit for Neural Machine Translation. March 2017. URL: https://arxiv.org/abs/1701.02810
[PairedCycleGAN]
Huiwen Chang; et al. PairedCycleGAN: Asymmetric Style Transfer for Applying and Removing Makeup. June 2018. URL: http://openaccess.thecvf.com/content_cvpr_2018/html/Chang_PairedCycleGAN_Asymmetric_Style_CVPR_2018_paper.html
[PoseNet]
Dan Oved. Real-time Human Pose Estimation in the Browser with TensorFlow.js. May 2018. URL: https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5
[POWERFUL-FEATURES]
Mike West. Secure Contexts. 10 November 2023. CR. URL: https://www.w3.org/TR/secure-contexts/
[RNNoise]
Jean-Marc Valin. Recurrent neural network for audio noise reduction. September 2017. URL: https://github.com/xiph/rnnoise
[SECURITY-PRIVACY-QUESTIONNAIRE]
Theresa O'Connor; Peter Snyder. Self-Review Questionnaire: Security and Privacy. 16 December 2021. NOTE. URL: https://www.w3.org/TR/security-privacy-questionnaire/
[SegAny]
Alexander Kirillov; et al. Segment Anything. April 2023. URL: https://arxiv.org/abs/2304.02643
[SRGAN]
Christian Ledig; et al. Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network. May 2017. URL: https://arxiv.org/abs/1609.04802
[SSD]
Wei Liu; et al. SSD: Single Shot MultiBox Detector. December 2016. URL: https://arxiv.org/abs/1512.02325
[T5-SMALL]
Colin Raffel; et al. Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer. June 2020. URL: https://jmlr.org/papers/volume21/20-074/20-074.pdf
[Video-Summarization-with-LSTM]
Ke Zhang; et al. Video summarization with long short-term memory. October 2016. URL: http://www-scf.usc.edu/~zhan355/ke_eccv2016.pdf
[WEBMACHINELEARNING-ETHICS]
Anssi Kostiainen. Ethical Principles for Web Machine Learning. 8 January 2024. NOTE. URL: https://www.w3.org/TR/webmachinelearning-ethics/
[Whisper]
Alec Radford; et al. Robust Speech Recognition via Large-Scale Weak Supervision. December 2022. URL: https://arxiv.org/abs/2212.04356
[YOLO]
Joseph Redmon; et al. You Only Look Once: Unified, Real-Time Object Detection. May 2016. URL: https://arxiv.org/abs/1506.02640

IDL Index

interface mixin NavigatorML {
  [SecureContext, SameObject] readonly attribute ML ml;
};
Navigator includes NavigatorML;
WorkerNavigator includes NavigatorML;

enum MLDeviceType {
  "cpu",
  "gpu"
};

enum MLPowerPreference {
  "default",
  "high-performance",
  "low-power"
};

dictionary MLContextOptions {
  MLDeviceType deviceType = "cpu";
  MLPowerPreference powerPreference = "default";
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface ML {
  Promise<MLContext> createContext(optional MLContextOptions options = {});
  Promise<MLContext> createContext(GPUDevice gpuDevice);

  [Exposed=(DedicatedWorker)]
  MLContext createContextSync(optional MLContextOptions options = {});
  [Exposed=(DedicatedWorker)]
  MLContext createContextSync(GPUDevice gpuDevice);
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLActivation {
};

typedef (GPUBuffer or GPUTexture) MLGPUResource;

typedef record<DOMString, MLGPUResource> MLNamedGPUResources;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLCommandEncoder {};

partial interface MLCommandEncoder {
  undefined initializeGraph(MLGraph graph);
};

partial interface MLCommandEncoder {
  undefined dispatch(MLGraph graph, MLNamedGPUResources inputs, MLNamedGPUResources outputs);
};

partial interface MLCommandEncoder {
  GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};

typedef record<DOMString, ArrayBufferView> MLNamedArrayBufferViews;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLContext {};

partial interface MLContext {
  [Exposed=(DedicatedWorker)]
  undefined computeSync(
      MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs);
};

dictionary MLComputeResult {
  MLNamedArrayBufferViews inputs;
  MLNamedArrayBufferViews outputs;
};

partial interface MLContext {
  Promise<MLComputeResult> compute(
      MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs);
};

partial interface MLContext {
  MLCommandEncoder createCommandEncoder();
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraph {};

typedef record<DOMString, MLOperand> MLNamedOperands;

dictionary MLBufferResourceView {
  required GPUBuffer resource;
  unsigned long long offset = 0;
  unsigned long long size;
};

typedef (ArrayBufferView or MLBufferResourceView) MLBufferView;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraphBuilder {
  // Construct the graph builder from the context.
  constructor(MLContext context);

  // Create an operand for a graph input.
  MLOperand input(DOMString name, MLOperandDescriptor descriptor);

  // Create an operand for a graph constant.
  MLOperand constant(MLOperandDescriptor descriptor, MLBufferView bufferView);

  // Create a single-value operand from the specified number of the specified type.
  MLOperand constant(double value, optional MLOperandDataType type = "float32");

  // Compile the graph up to the specified output operands asynchronously.
  Promise<MLGraph> build(MLNamedOperands outputs);

  // Compile the graph up to the specified output operands synchronously.
  [Exposed=(DedicatedWorker)]
  MLGraph buildSync(MLNamedOperands outputs);
};

dictionary MLArgMinMaxOptions {
  sequence<unsigned long> axes = null;
  boolean keepDimensions = false;
  boolean selectLastIndex = false;
};

partial interface MLGraphBuilder {
  MLOperand argMin(MLOperand input, optional MLArgMinMaxOptions options = {});
  MLOperand argMax(MLOperand input, optional MLArgMinMaxOptions options = {});
};

dictionary MLBatchNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  unsigned long axis = 1;
  float epsilon = 1e-5;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance,
                             optional MLBatchNormalizationOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand cast(MLOperand input, MLOperandDataType type);
};

dictionary MLClampOptions {
  float minValue;
  float maxValue;
};

partial interface MLGraphBuilder {
  MLOperand clamp(MLOperand input, optional MLClampOptions options = {});
  MLActivation clamp(optional MLClampOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand concat(sequence<MLOperand> inputs, unsigned long axis);
};

enum MLConv2dFilterOperandLayout {
  "oihw",
  "hwio",
  "ohwi",
  "ihwo"
};

enum MLAutoPad {
  "explicit",
  "same-upper",
  "same-lower"
};

dictionary MLConv2dOptions {
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  MLAutoPad autoPad = "explicit";
  unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConv2dFilterOperandLayout filterLayout = "oihw";
  MLOperand bias;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand conv2d(MLOperand input, MLOperand filter, optional MLConv2dOptions options = {});
};

enum MLConvTranspose2dFilterOperandLayout {
  "iohw",
  "hwoi",
  "ohwi"
};

dictionary MLConvTranspose2dOptions {
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  sequence<unsigned long> outputPadding;
  sequence<unsigned long> outputSizes;
  MLAutoPad autoPad = "explicit";
  unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConvTranspose2dFilterOperandLayout filterLayout = "iohw";
  MLOperand bias;
  MLActivation activation;
};

partial interface MLGraphBuilder {
  MLOperand convTranspose2d(MLOperand input, MLOperand filter,
                            optional MLConvTranspose2dOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand add(MLOperand a, MLOperand b);
  MLOperand sub(MLOperand a, MLOperand b);
  MLOperand mul(MLOperand a, MLOperand b);
  MLOperand div(MLOperand a, MLOperand b);
  MLOperand max(MLOperand a, MLOperand b);
  MLOperand min(MLOperand a, MLOperand b);
  MLOperand pow(MLOperand a, MLOperand b);
};

partial interface MLGraphBuilder {
  MLOperand equal(MLOperand a, MLOperand b);
  MLOperand greater(MLOperand a, MLOperand b);
  MLOperand greaterOrEqual(MLOperand a, MLOperand b);
  MLOperand lesser(MLOperand a, MLOperand b);
  MLOperand lesserOrEqual(MLOperand a, MLOperand b);
  MLOperand not(MLOperand a);
};

partial interface MLGraphBuilder {
  MLOperand abs(MLOperand input);
  MLOperand ceil(MLOperand input);
  MLOperand cos(MLOperand input);
  MLOperand erf(MLOperand input);
  MLOperand exp(MLOperand input);
  MLOperand floor(MLOperand input);
  MLOperand identity(MLOperand input);
  MLOperand log(MLOperand input);
  MLOperand neg(MLOperand input);
  MLOperand reciprocal(MLOperand input);
  MLOperand sin(MLOperand input);
  MLOperand sqrt(MLOperand input);
  MLOperand tan(MLOperand input);
};

dictionary MLEluOptions {
  float alpha = 1;
};

partial interface MLGraphBuilder {
  MLOperand elu(MLOperand input, optional MLEluOptions options = {});
  MLActivation elu(optional MLEluOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand expand(MLOperand input, sequence<unsigned long> newShape);
};

dictionary MLGatherOptions {
  unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand gather(MLOperand input, MLOperand indices, optional MLGatherOptions options = {});
};

dictionary MLGemmOptions {
  MLOperand c;
  float alpha = 1.0;
  float beta = 1.0;
  boolean aTranspose = false;
  boolean bTranspose = false;
};

partial interface MLGraphBuilder {
  MLOperand gemm(MLOperand a, MLOperand b, optional MLGemmOptions options = {});
};

enum MLGruWeightLayout {
  "zrn",  // update-reset-new gate ordering
  "rzn"   // reset-update-new gate ordering
};

enum MLRecurrentNetworkDirection {
  "forward",
  "backward",
  "both"
};

dictionary MLGruOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand initialHiddenState;
  boolean resetAfter = true;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> gru(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                          unsigned long steps, unsigned long hiddenSize,
                          optional MLGruOptions options = {});
};

dictionary MLGruCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  boolean resetAfter = true;
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  MLOperand gruCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                    MLOperand hiddenState, unsigned long hiddenSize,
                    optional MLGruCellOptions options = {});
};

dictionary MLHardSigmoidOptions {
  float alpha = 0.2;
  float beta = 0.5;
};

partial interface MLGraphBuilder {
  MLOperand hardSigmoid(MLOperand input, optional MLHardSigmoidOptions options = {});
  MLActivation hardSigmoid(optional MLHardSigmoidOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand hardSwish(MLOperand input);
  MLActivation hardSwish();
};

dictionary MLInstanceNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  float epsilon = 1e-5;
  MLInputOperandLayout layout = "nchw";
};

partial interface MLGraphBuilder {
  MLOperand instanceNormalization(MLOperand input,
                                optional MLInstanceNormalizationOptions options = {});
};

dictionary MLLayerNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  sequence<unsigned long> axes;
  float epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand layerNormalization(MLOperand input, optional MLLayerNormalizationOptions options = {});
};

dictionary MLLeakyReluOptions {
  float alpha = 0.01;
};

partial interface MLGraphBuilder {
  MLOperand leakyRelu(MLOperand input, optional MLLeakyReluOptions options = {});
  MLActivation leakyRelu(optional MLLeakyReluOptions options = {});
};

dictionary MLLinearOptions {
  float alpha = 1;
  float beta = 0;
};

partial interface MLGraphBuilder {
  MLOperand linear(MLOperand input, optional MLLinearOptions options = {});
  MLActivation linear(optional MLLinearOptions options = {});
};

enum MLLstmWeightLayout {
  "iofg", // input-output-forget-cell gate ordering
  "ifgo"  // input-forget-cell-output gate ordering
};

dictionary MLLstmOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLOperand initialHiddenState;
  MLOperand initialCellState;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstm(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                           unsigned long steps, unsigned long hiddenSize,
                           optional MLLstmOptions options = {});
};

dictionary MLLstmCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstmCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight,
                               MLOperand hiddenState, MLOperand cellState, unsigned long hiddenSize,
                               optional MLLstmCellOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand matmul(MLOperand a, MLOperand b);
};

enum MLPaddingMode {
  "constant",
  "edge",
  "reflection",
  "symmetric"
};

dictionary MLPadOptions {
  MLPaddingMode mode = "constant";
  float value = 0;
};

partial interface MLGraphBuilder {
  MLOperand pad(MLOperand input,
                sequence<unsigned long> beginningPadding,
                sequence<unsigned long> endingPadding,
                optional MLPadOptions options = {});
};

enum MLRoundingType {
  "floor",
  "ceil"
};

dictionary MLPool2dOptions {
  sequence<unsigned long> windowDimensions;
  sequence<unsigned long> padding;
  sequence<unsigned long> strides;
  sequence<unsigned long> dilations;
  MLAutoPad autoPad = "explicit";
  MLInputOperandLayout layout = "nchw";
  MLRoundingType roundingType = "floor";
  sequence<unsigned long> outputSizes;
};

partial interface MLGraphBuilder {
  MLOperand averagePool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand l2Pool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand maxPool2d(MLOperand input, optional MLPool2dOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand prelu(MLOperand input, MLOperand slope);
};

dictionary MLReduceOptions {
  sequence<unsigned long> axes = null;
  boolean keepDimensions = false;
};

partial interface MLGraphBuilder {
  MLOperand reduceL1(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceL2(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSumExp(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMax(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMean(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMin(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceProduct(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSumSquare(MLOperand input, optional MLReduceOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand relu(MLOperand input);
  MLActivation relu();
};

enum MLInterpolationMode {
  "nearest-neighbor",
  "linear"
};

dictionary MLResample2dOptions {
  MLInterpolationMode mode = "nearest-neighbor";
  sequence<float> scales;
  sequence<unsigned long> sizes;
  sequence<unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand resample2d(MLOperand input, optional MLResample2dOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand reshape(MLOperand input, sequence<unsigned long> newShape);
};

partial interface MLGraphBuilder {
  MLOperand sigmoid(MLOperand input);
  MLActivation sigmoid();
};

partial interface MLGraphBuilder {
  MLOperand slice(MLOperand input, sequence<unsigned long> starts, sequence<unsigned long> sizes);
};

partial interface MLGraphBuilder {
  MLOperand softmax(MLOperand input);
  MLActivation softmax();
};

dictionary MLSoftplusOptions {
  float steepness = 1;
};

partial interface MLGraphBuilder {
  MLOperand softplus(MLOperand input, optional MLSoftplusOptions options = {});
  MLActivation softplus(optional MLSoftplusOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand softsign(MLOperand input);
  MLActivation softsign();
};

dictionary MLSplitOptions {
  unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> split(MLOperand input,
                          (unsigned long or sequence<unsigned long>) splits,
                          optional MLSplitOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand tanh(MLOperand input);
  MLActivation tanh();
};

dictionary MLTransposeOptions {
  sequence<unsigned long> permutation;
};

partial interface MLGraphBuilder {
  MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {});
};

dictionary MLTriangularOptions {
  boolean upper = true;
  long diagonal = 0;
};

partial interface MLGraphBuilder {
  MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {});
};

partial interface MLGraphBuilder {
  MLOperand where(MLOperand condition, MLOperand input, MLOperand other);
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLOperand {};

partial interface MLOperand {
  MLOperandDataType dataType();
};

partial interface MLOperand {
  sequence<unsigned long> shape();
};

enum MLInputOperandLayout {
  "nchw",
  "nhwc"
};

enum MLOperandDataType {
  "float32",
  "float16",
  "int32",
  "uint32",
  "int64",
  "uint64",
  "int8",
  "uint8"
};

dictionary MLOperandDescriptor {
  // The operand type.
  required MLOperandDataType dataType;

  // The dimensions field is only required for tensor operands.
  sequence<unsigned long> dimensions;
};

Issues Index

Document operations susceptible to out-of-bounds access as a guidance to implementers.
Investigate side channel attack feasibility considering the current state where CPU is shared between processes running renderers.
Hinting partially mitigates the concern. Investigate additional mitigations.
The shape of input, weight or recurrentWeight could be also checked here.