# --------------------------------------------------------------------------
# ⚠️ WARNING - AUTO-GENERATED CODE - DO NOT EDIT ⚠️
# ⚙️ Generated by 'python -m opgen'
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# --------------------------------------------------------------------------
# pylint: disable=W0221,W0222,R0901,W0237
# mypy: disable-error-code=override
# ruff: noqa: D402
# --------------------------------------------------------------------------

from __future__ import annotations

from typing import Optional, Sequence, Tuple, TypeVar

from onnx import GraphProto
from onnx.defs import get_schema
from typing_extensions import TypeAlias

from onnxscript.onnx_opset._impl.opset7 import Opset7
from onnxscript.onnx_types import (
    BOOL,
    COMPLEX64,
    COMPLEX128,
    DOUBLE,
    FLOAT,
    FLOAT16,
    INT8,
    INT16,
    INT32,
    INT64,
    STRING,
    UINT8,
    UINT16,
    UINT32,
    UINT64,
)
from onnxscript.values import Op, Opset


class Opset8(Opset7):
    def __new__(cls):
        return Opset.__new__(cls, "", 8)

    T_Expand = TypeVar(
        "T_Expand",
        BOOL,
        COMPLEX128,
        COMPLEX64,
        DOUBLE,
        FLOAT,
        FLOAT16,
        INT16,
        INT32,
        INT64,
        INT8,
        STRING,
        UINT16,
        UINT32,
        UINT64,
        UINT8,
    )

    def Expand(self, input: T_Expand, shape: INT64) -> T_Expand:
        r"""[🌐 Expand(8)](https://onnx.ai/onnx/operators/onnx__Expand.html#expand-8 "Online Documentation")


        Broadcast the input tensor following the given shape and the broadcast rule.
        The broadcast rule is similar to numpy.array(input) * numpy.ones(shape):
        Dimensions are right alignment;
        Two corresponding dimensions must have the same value, or one of them is equal to 1.
        Also, this operator is similar to numpy.broadcast_to(input, shape),
        but the major difference is numpy.broadcast_to() does not allow shape to be smaller than input.size().
        It is possible that the output.shape is not equal to shape, when some dimensions in shape is equal to 1,
        or the shape.ndim < input.shape.ndim.


        Args:
            input: Input tensor

            shape: A 1-D tensor indicates the shape you want to expand to, following the
                broadcast rule
        """

        schema = get_schema("Expand", 8, "")
        op = Op(self, "Expand", schema)
        return op(*self._prepare_inputs(schema, input, shape))

    T_Max = TypeVar("T_Max", DOUBLE, FLOAT, FLOAT16)

    def Max(self, *data_0: T_Max) -> T_Max:
        r"""[🌐 Max(8)](https://onnx.ai/onnx/operators/onnx__Max.html#max-8 "Online Documentation")


        Element-wise max of each of the input tensors (with Numpy-style broadcasting support).
        All inputs and outputs must have the same data type.
        This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check `Broadcasting in ONNX <https://github.com/onnx/onnx/blob/master/docs/Broadcasting.md>`_.


        Args:
            data_0: (variadic) List of tensors for max.
        """

        schema = get_schema("Max", 8, "")
        op = Op(self, "Max", schema)
        return op(*self._prepare_inputs(schema, *data_0))

    T_MaxPool = TypeVar("T_MaxPool", DOUBLE, FLOAT, FLOAT16)

    I_MaxPool: TypeAlias = INT64

    def MaxPool(
        self,
        X: T_MaxPool,
        *,
        auto_pad: str = "NOTSET",
        kernel_shape: Sequence[int],
        pads: Optional[Sequence[int]] = None,
        storage_order: int = 0,
        strides: Optional[Sequence[int]] = None,
    ) -> Tuple[T_MaxPool, I_MaxPool]:
        r"""[🌐 MaxPool(8)](https://onnx.ai/onnx/operators/onnx__MaxPool.html#maxpool-8 "Online Documentation")


         MaxPool consumes an input tensor X and applies max pooling across
         the tensor according to kernel sizes, stride sizes, and pad lengths.
         max pooling consisting of computing the max on all values of a
         subset of the input tensor according to the kernel size and downsampling the
         data into the output tensor Y for further processing. The output spatial shape will be following:
         ```
         output_spatial_shape[i] = floor((input_spatial_shape[i] + pad_shape[i] - kernel_spatial_shape[i]) / strides_spatial_shape[i] + 1)

         * pad_shape[i] is sum of pads along axis i
         ```

         `auto_pad` is a DEPRECATED attribute. If you are using them currently, the output spatial shape will be following:
         ```
         VALID: output_spatial_shape[i] = ceil((input_spatial_shape[i] - kernel_spatial_shape[i] + 1) / strides_spatial_shape[i])
         SAME_UPPER or SAME_LOWER: output_spatial_shape[i] = ceil(input_spatial_shape[i] / strides_spatial_shape[i])
         ```
         And pad shape will be following if `SAME_UPPER` or `SAME_LOWER`:
         ```
         pad_shape[i] = (output_spatial_shape[i] - 1) * strides_spatial_shape[i] + kernel_spatial_shape[i] - input_spatial_shape[i]
         ```
         The output of each pooling window is maximum number of elements exclude pad.


        Args:
            X: Input data tensor from the previous operator; dimensions for image case
                are (N x C x H x W), where N is the batch size, C is the number of
                channels, and H and W are the height and the width of the data. For non
                image case, the dimensions are in the form of (N x C x D1 x D2 ... Dn),
                where N is the batch size. Optionally, if dimension denotation is in
                effect, the operation expects the input data tensor to arrive with the
                dimension denotation of [DATA_BATCH, DATA_CHANNEL, DATA_FEATURE,
                DATA_FEATURE ...].

            auto_pad: auto_pad must be either NOTSET, SAME_UPPER, SAME_LOWER or VALID.
                Where default value is NOTSET, which means explicit padding is used.
                SAME_UPPER or SAME_LOWER mean pad the input so that the output spatial
                size match the input.In case of odd number add the extra padding at the
                end for SAME_UPPER and at the beginning for SAME_LOWER. VALID mean no
                padding.

            kernel_shape: The size of the kernel along each axis.

            pads: Padding for the beginning and ending along each spatial axis, it can
                take any value greater than or equal to 0. The value represent the
                number of pixels added to the beginning and end part of the
                corresponding axis. `pads` format should be as follow [x1_begin,
                x2_begin...x1_end, x2_end,...], where xi_begin the number of pixels
                added at the beginning of axis `i` and xi_end, the number of pixels
                added at the end of axis `i`. This attribute cannot be used
                simultaneously with auto_pad attribute. If not present, the padding
                defaults to 0 along start and end of each spatial axis.

            storage_order: The storage order of the tensor. 0 is row major, and 1 is
                column major.

            strides: Stride along each spatial axis.
        """

        schema = get_schema("MaxPool", 8, "")
        op = Op(self, "MaxPool", schema)
        return op(
            *self._prepare_inputs(schema, X),
            auto_pad=auto_pad,
            kernel_shape=kernel_shape,
            pads=pads,
            storage_order=storage_order,
            strides=strides,
        )

    T_Mean = TypeVar("T_Mean", DOUBLE, FLOAT, FLOAT16)

    def Mean(self, *data_0: T_Mean) -> T_Mean:
        r"""[🌐 Mean(8)](https://onnx.ai/onnx/operators/onnx__Mean.html#mean-8 "Online Documentation")


        Element-wise mean of each of the input tensors (with Numpy-style broadcasting support).
        All inputs and outputs must have the same data type.
        This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check `Broadcasting in ONNX <https://github.com/onnx/onnx/blob/master/docs/Broadcasting.md>`_.


        Args:
            data_0: (variadic) List of tensors for mean.
        """

        schema = get_schema("Mean", 8, "")
        op = Op(self, "Mean", schema)
        return op(*self._prepare_inputs(schema, *data_0))

    T_Min = TypeVar("T_Min", DOUBLE, FLOAT, FLOAT16)

    def Min(self, *data_0: T_Min) -> T_Min:
        r"""[🌐 Min(8)](https://onnx.ai/onnx/operators/onnx__Min.html#min-8 "Online Documentation")


        Element-wise min of each of the input tensors (with Numpy-style broadcasting support).
        All inputs and outputs must have the same data type.
        This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check `Broadcasting in ONNX <https://github.com/onnx/onnx/blob/master/docs/Broadcasting.md>`_.


        Args:
            data_0: (variadic) List of tensors for min.
        """

        schema = get_schema("Min", 8, "")
        op = Op(self, "Min", schema)
        return op(*self._prepare_inputs(schema, *data_0))

    I_Scan: TypeAlias = INT64

    V_Scan = TypeVar(
        "V_Scan",
        BOOL,
        COMPLEX128,
        COMPLEX64,
        DOUBLE,
        FLOAT,
        FLOAT16,
        INT16,
        INT32,
        INT64,
        INT8,
        STRING,
        UINT16,
        UINT32,
        UINT64,
        UINT8,
    )

    def Scan(
        self,
        sequence_lens: Optional[I_Scan],
        *initial_state_and_scan_inputs: V_Scan,
        body: GraphProto,
        directions: Optional[Sequence[int]] = None,
        num_scan_inputs: int,
    ) -> V_Scan:
        r"""[🌐 Scan(8)](https://onnx.ai/onnx/operators/onnx__Scan.html#scan-8 "Online Documentation")


        Scan can be used to iterate over one or more scan_input tensors,
        constructing zero or more scan_output tensors. It combines ideas from general recurrences,
        functional programming constructs such as scan, fold, map, and zip, and is intended to enable
        generalizations of RNN-like constructs for sequence-to-sequence processing.
        Other tensors (referred to as state_variables here) can be used to carry a state
        when iterating from one element to another (similar to hidden-state in RNNs, also referred
        to as loop-carried dependences in the context of loops). All these tensors are required to
        have the same shape in each iteration of the loop (a restriction imposed to enable efficient
        memory allocation). Many common usages involve a single scan_input tensor (where functionality
        similar to scan, fold and map can be obtained). When more than one scan_input is used,
        a behavior similar to zip is obtained.

        The attribute body must be a graph, specifying the computation to be performed in
        every iteration. It takes as input the current values of the state_variables and
        the current iterated element of the scan_inputs. It must return the (updated) values
        of the state_variables and zero or more scan_output_element tensors. The values of the
        scan_output_element tensors are concatenated over all the iterations to produce the
        scan_output values of the scan construct (similar to the concatenated intermediate
        hidden-state values of RNN-like constructs).

        The scan operation returns the final values of the state_variables as well as the
        scan_outputs.

        The operation supports batching, and the batch-axis is required to be 0.
        When multiple scan_input tensors are used, they must all have the same batch-size,
        and they must all have the same maximum-sequence-length (the dimensionality of the
        sequence axis or scan axis). The sequence axis or scan axis is required to be 1.

        The operation has an optional sequence_lens input (of shape [BATCH_SIZE]) to
        allow variable length sequences of length <= the maximum-sequence-length. If this
        input is not specified, all sequences are assumed to be of length equal to
        maximum-sequence-length. For variable length input sequences, the scan_outputs
        will consist of a sequence of same length as the input, padded to the
        maximum-sequence-length.

        The optional attribute directions can be used to scan a sequence in the reverse direction.
        If this attribute is omitted, all sequences are scanned in the forward direction.
        A bidirectional scan be performed by specifying the same tensor input twice in the
        scan_inputs, once with a forward direction, and once with a backward direction.

        Note that because of the ONNX restriction that only the last parameter of an operator can
        be variadic, the initial-states and scan-inputs are listed together as one input parameter.
        Similarly, the final-states and scan-outputs are listed together as one output parameter.
        The attribute num_scan_inputs indicates the number M of scan-inputs.

        The behavior of

            Scan <
                num_scan_inputs = m,
                body = loop-body
            > (sequence_lengths, init_1, ..., init_n, scan_1, ..., scan_m)

        is equivalent to the following pseudo-code:

            // T.shape[0] denotes the batch-size of T
            // The batch-size of scan_1, ..., scan_m are all required to be equal
            batch_size = scan_1.shape[0];

            // scan_i.shape[1] denotes the (max) sequence-length of scan_i
            // scan_i.shape[1] is required to be equal to scan_j.shape[1] for all i,j.
            max_sequence_length = scan_1.shape[1];

            for (int batch = 0; batch < batch_size; ++batch) {
                // initialize state-variables
                st_1 = init_1; ... st_n = init_n;
                // initialize scan-output variables: [] denotes an empty tensor
                scan_out_1 = []; ...; scan_out_k = [];
                // identify number of iterations:
                N = (sequence_lengths specified) ? sequence_lengths[batch] : max_sequence_length;

                // execute loop
                for (int t = 0; t < N; ++t) {
                    // generate the scan-input elements: the notation T<axis=k>[t] indicates the sub-tensor
                    // of rank one less than T obtained by indexing T at position t along axis k.
                    si_1 = (scan_1<axis=0>[batch])<axis=1>[t];
                    ... ;
                    si_m = (scan_m<axis=0>[batch])<axis=1>[t];
                    // execute loop-body
                    st_1, ..., st_n, so_1, ..., so_k = loop-body(st_1, ..., st_n, si_1, ..., si_m)
                    // accumulate the scan-output elements
                    scan_out_1 = Concat<axis=0>(scan_out_1, so_1); ... ; scan_out_k = Concat<axis=0>(scan_out_k, so_k);
                }
                // accumulate the outputs for this batch:
                bst_1[batch] = st_1; ..., bst_n[batch] = st_n;
                // Note scan-outputs will have size max_sequence_length, but only first N values will be meaningful.
                // The remaining values have an undefined value.
                b_scan_out_1[batch] = scan_out_1; ...; b_scan_out_k[batch] = scan_out_k;
            }
            return bst_1, ..., bst_n, b_scan_out_1, ..., b_scan_out_k;



        *Sample usage: Encoding RNN using a Scan*

        The following example shows how a simple RNN over an input tensor %X, with weight tensor %Wi,
        recurrence weight tensor %Ri, bias tensors %Wbi and %Rbi, and initial hidden-state %H_0 can
        be encoded as a ScanLoop. Note that the loop-body is a nested graph, and it directly computes
        %Wi, %Ri, %Wbi, and %Rbi (typically constants or initializers in the body graph). If these
        values are computed in the outer graph, they need to be passed in as extra state_variables.

            graph rnn-encoding {
              %H_0 = ...
              %X = ...
              %Y_h, %Y = Scan[body = <graph rnn-cell-1>, num_scan_inputs=1]("", %H_0, %X)
              return %Y, %Y_h
            }

            graph rnn-cell-1 (
              %H_tminus1[FLOAT, tensor]
              %X_t[FLOAT, tensor]
            ) {
              %Wi = ...
              %Ri = ...
              %Wbi = ...
              %Rbi = ...
              %t1 = X_t * (Wi^T)
              %t2 = H_tminus1*(Ri^T)
              %t3 = Add(%t1, %t2)
              %t4 = Add(%t3, %Wbi)
              %t5 = Add(%t4, %Rbi)
              %Ht = Tanh(%t5)
              %Accumulate = Identity(%Ht)
              return %Ht, %Accumulate
            }



        Args:
            sequence_lens: (optional) Optional tensor specifying lengths of the
                sequences in a batch. If this input is not specified, all sequences are
                assumed to be of the maximum sequence length (the dimension of the
                sequence axis of the scan_input tensors).

            initial_state_and_scan_inputs: (variadic, heterogeneous) Initial values of
                the loop's N state variables followed by M scan_inputs

            body: The graph run each iteration. It has N+M inputs: (loop state
                variables..., scan_input_elts...). It has N+K outputs: (loop state
                variables..., scan_output_elts...). Each scan_output is created by
                concatenating the value of the specified scan_output_elt value at the
                end of each iteration of the loop. It is an error if the dimensions of
                these values change across loop iterations.

            directions: An optional list of M flags. The i-th element of the list
                specifies the direction to be scanned for the i-th scan_input tensor: 0
                indicates forward direction and 1 indicates reverse direction. If
                omitted, all scan_input tensors will be scanned in the forward
                direction.

            num_scan_inputs: An attribute specifying the number of scan_inputs M.
        """

        schema = get_schema("Scan", 8, "")
        op = Op(self, "Scan", schema)
        return op(
            *self._prepare_inputs(schema, sequence_lens, *initial_state_and_scan_inputs),
            body=body,
            directions=directions,
            num_scan_inputs=num_scan_inputs,
        )

    T_Sum = TypeVar("T_Sum", DOUBLE, FLOAT, FLOAT16)

    def Sum(self, *data_0: T_Sum) -> T_Sum:
        r"""[🌐 Sum(8)](https://onnx.ai/onnx/operators/onnx__Sum.html#sum-8 "Online Documentation")


        Element-wise sum of each of the input tensors (with Numpy-style broadcasting support).
        All inputs and outputs must have the same data type.
        This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check `Broadcasting in ONNX <https://github.com/onnx/onnx/blob/master/docs/Broadcasting.md>`_.


        Args:
            data_0: (variadic) List of tensors for sum.
        """

        schema = get_schema("Sum", 8, "")
        op = Op(self, "Sum", schema)
        return op(*self._prepare_inputs(schema, *data_0))
