o
    XiS                     @  s  d Z ddlmZ g dZddlZddlmZmZmZ ddl	m
Z
 ddlZddlZddlmZmZmZmZmZ e
eeeee ee ee ejejejejeej ejejeej df ZeeZ d=ddZ!	d>d?ddZ"d@ddZ#	dAdBd!d"Z$d#d$dCd)d*Z%dDd5d6Z&	dAdEd;d<Z'dS )FzConvenience methods for constructing and manipulating the IR.

This is an internal only module. We should choose to expose some of the methods
in convenience.py after they are proven to be useful.
    )annotations)convert_attributeconvert_attributesreplace_all_uses_withcreate_value_mappingreplace_nodes_and_valuesget_const_tensorN)IterableMappingSequence)Union)_core_enums
_protocolsserde	traversalattrSupportedAttrTypesreturn_enums.AttributeTypec                 C  sv  t | tr	tjjS t | trtjjS t | trtjjS t | t	j
r$| jS t | t	jtjtjfr3tjjS t | t	jtjtjfrBtjjS t | t	jt	jt	jtjfrStjjS t | tr| sctd tjjS tdd | D rptjjS tdd | D r}tjj S tdd | D rtjj!S tdd | D rtjj"S tdd | D rtjj#S tdd | D rtjj$S t%d	t|  d
)z@Infer the attribute type based on the type of the Python object.z{Attribute type is ambiguous because it is an empty sequence. Please create an Attr with an explicit type. Defaulted to INTSc                 s      | ]}t |tV  qd S N)
isinstanceint.0x r   Q/home/ubuntu/.local/lib/python3.10/site-packages/onnx_ir/_convenience/__init__.py	<genexpr>N       z(_infer_attribute_type.<locals>.<genexpr>c                 s  r   r   )r   floatr   r   r   r   r   P   r    c                 s  r   r   )r   strr   r   r   r   r   R   r    c                 s  &    | ]}t |tjtjtjfV  qd S r   )r   r   
TensorBaseonnxTensorProtor   TensorProtocolr   r   r   r   r   T   
    
c                 s  r#   r   )r   r   Graphr%   
GraphProtor   GraphProtocolr   r   r   r   r   Y   r(   c                 s  s*    | ]}t |tjtjtjtjfV  qd S r   )r   r   
TensorTypeSequenceTypeOptionalTyper   TypeProtocolr   r   r   r   r   ^   s    

Unsupported attribute type: '')&r   r   r   AttributeTypeINTr!   FLOATr"   STRINGr   Attrtyper)   r%   r*   r   r+   GRAPHr$   r&   r'   TENSORr,   r-   r.   r/   
TYPE_PROTOr   loggerwarningINTSallFLOATSSTRINGSTENSORSGRAPHSTYPE_PROTOS	TypeError)r   r   r   r   _infer_attribute_type3   sT   




rE   namer"   	attr_type_enums.AttributeType | None
_core.Attrc              	   C  s  |du r|du rt dt| |dS t|tjrA|j| kr*t d|j d|  d|dur?|j|kr?t d|j d| d|S |du rIt|}|tjj	krUt
| |S |tjjkrat| |S |tjjkrmt| |S |tjjkryt| |S |tjjkrt| |S |tjjkrt| |S |tjjkrt|tjtjfrt| |S t|tjrt| t|S |tjjkrg }|D ]}t|tjr|t| t| q|| qt | |S |tjj!krt|tj"rt#|}t$| |S |tjj%kr"g }|D ]}t|tj"r|t#| q|| qt&| |S |tjj'kr/t(| |S |tjj)kr<t*| |S t+dt| d)	ar  Convert a Python object to a _core.Attr object.

    This method is useful when constructing nodes with attributes. It infers the
    attribute type based on the type of the Python value.

    Args:
        name: The name of the attribute.
        attr: The value of the attribute.
        attr_type: The type of the attribute. This is required when attr is None.
            When provided, it overrides the inferred type.

    Returns:
        A ``Attr`` object.

    Raises:
        ValueError: If ``attr`` is ``None`` and ``attr_type`` is not provided.
        TypeError: If the type of the attribute is not supported.
    Nz,attr_type must be provided when attr is NonezAttribute name 'z ' does not match provided name 'r1   zAttribute type 'z ' does not match provided type 'r0   ),
ValueErrorr   r6   r   rF   r7   rE   r   r2   r3   	AttrInt64r4   AttrFloat32r5   
AttrStringr=   
AttrInt64sr?   AttrFloat32sr@   AttrStringsr9   r$   r   r'   
AttrTensorr%   r&   r   deserialize_tensorrA   appendAttrTensorsr8   r*   deserialize_graph	AttrGraphrB   
AttrGraphsr:   AttrTypeProtorC   AttrTypeProtosrD   )rF   r   rG   tensorstgraphsgraphr   r   r   r   n   sp   

r   attrs Mapping[str, SupportedAttrTypes]list[_core.Attr]c                 C  s2   g }|   D ]\}}|dur|t|| q|S )aX  Convert a dictionary of attributes to a list of _core.Attr objects.

    It infers the attribute type based on the type of the value. The supported
    types are: int, float, str, Sequence[int], Sequence[float], Sequence[str],
    :class:`_core.Tensor`, and :class:`_core.Attr`::

        >>> import onnx_ir as ir
        >>> import onnx
        >>> import numpy as np
        >>> attrs = {
        ...     "int": 1,
        ...     "float": 1.0,
        ...     "str": "hello",
        ...     "ints": [1, 2, 3],
        ...     "floats": [1.0, 2.0, 3.0],
        ...     "strings": ["hello", "world"],
        ...     "tensor": ir.Tensor(np.array([1.0, 2.0, 3.0])),
        ...     "tensor_proto":
        ...         onnx.TensorProto(
        ...             dims=[3],
        ...             data_type=onnx.TensorProto.FLOAT,
        ...             float_data=[1.0, 2.0, 3.0],
        ...             name="proto",
        ...         ),
        ...     "graph": ir.Graph([], [], nodes=[], name="graph0"),
        ...     "graphs": [ir.Graph([], [], nodes=[], name="graph1"), ir.Graph([], [], nodes=[], name="graph2")],
        ...     "type_proto": ir.TensorType(ir.DataType.FLOAT),
        ...     "type_protos": [ir.TensorType(ir.DataType.FLOAT), ir.TensorType(ir.DataType.FLOAT)],
        ... }
        >>> convert_attributes(attrs)
        [Attr('int', INT, 1), Attr('float', FLOAT, 1.0), Attr('str', STRING, 'hello'), Attr('ints', INTS, (1, 2, 3)), Attr('floats', FLOATS, (1.0, 2.0, 3.0)), Attr('strings', STRINGS, ('hello', 'world')), Attr('tensor', TENSOR, Tensor<DOUBLE,[3]>(array([1., 2., 3.]), name=None)), Attr('tensor_proto', TENSOR, TensorProtoTensor<FLOAT,[3]>(array([1., 2., 3.], dtype=float32), name='proto')), Attr('graph', GRAPH, Graph(
            name='graph0',
            inputs=(
        <BLANKLINE>
            ),
            outputs=(
        <BLANKLINE>
            ),
            len()=0
        )), Attr('graphs', GRAPHS, (Graph(
            name='graph1',
            inputs=(
        <BLANKLINE>
            ),
            outputs=(
        <BLANKLINE>
            ),
            len()=0
        ), Graph(
            name='graph2',
            inputs=(
        <BLANKLINE>
            ),
            outputs=(
        <BLANKLINE>
            ),
            len()=0
        ))), Attr('type_proto', TYPE_PROTO, Tensor(FLOAT)), Attr('type_protos', TYPE_PROTOS, (Tensor(FLOAT), Tensor(FLOAT)))]

    .. important::
        An empty sequence should be created with an explicit type by initializing
        an Attr object with an attribute type to avoid type ambiguity. For example::

            ir.Attr("empty", [], type=ir.AttributeType.INTS)

    Args:
        attrs: A dictionary of {<attribute name>: <python objects>} to convert.

    Returns:
        A list of :class:`_core.Attr` objects.

    Raises:
        TypeError: If an attribute type is not supported.
    N)itemsrS   r   )r^   
attributesrF   r   r   r   r   r      s   Mr   Fvalues=_protocols.ValueProtocol | Sequence[_protocols.ValueProtocol]replacementsreplace_graph_outputsboolNonec                 C  s^   t | ts| f} t |ts|f}t| t|krtdt| |D ]\}}|j||d q!dS )az
  Replace all uses of the given values with the replacements.

    This is useful when nodes in the graph are replaced with new nodes, where
    the old users need to be updated to use the outputs of the new nodes.

    For example, suppose we have the following graph::

        A -> {B, C}

    We want to replace the node A with a new node D::

        >>> import onnx_ir as ir
        >>> input = ir.val("input")
        >>> node_a = ir.Node("", "A", [input])
        >>> node_b = ir.Node("", "B", node_a.outputs)
        >>> node_c = ir.Node("", "C", node_a.outputs)
        >>> node_d = ir.Node("", "D", [input])
        >>> replace_all_uses_with(node_a.outputs, node_d.outputs)
        >>> len(node_b.inputs)
        1
        >>> node_b.inputs[0].producer().op_type
        'D'
        >>> len(node_c.inputs)
        1
        >>> node_c.inputs[0].producer().op_type
        'D'
        >>> len(node_a.outputs[0].uses())
        0

    When values and replacements are sequences, they are zipped into pairs. All
    users of the first value is replaced with the first replacement, and so on.

    .. note::
        Be sure to remove the old nodes from the graph using ``graph.remove()``
        if they are no longer needed, or use :class:`onnx_ir.passes.common.RemoveUnusedNodesPass`
        to remove all unused nodes in the graph.

    .. tip::
        **Handling graph outputs**

        To also replace graph outputs that reference the values being replaced, either
        set ``replace_graph_outputs`` to True, or manually update the graph outputs
        before calling this function to avoid an error being raised when ``replace_graph_outputs=False``.

        Be careful when a value appears multiple times in the graph outputs -
        this is invalid. An identity node will need to be added on each duplicated
        outputs to ensure a valid ONNX graph.

        You may also want to assign the name of this value to the replacement value
        to maintain the name when it is a graph output.

    .. versionadded:: 0.1.12
        The ``replace_graph_outputs`` parameter is added.

    .. versionadded:: 0.1.12
        ValueError is raised when ``replace_graph_outputs`` is False && when the value to
        replace is a graph output.

    Args:
        values: The value or values to be replaced.
        replacements: The new value or values to use as inputs.
        replace_graph_outputs: If True, graph outputs that reference the values
            being replaced will also be updated to reference the replacements.

    Raises:
        ValueError: When ``replace_graph_outputs`` is False && when the value to
            replace is a graph output.
    z1The number of values and replacements must match.rf   N)r   r   lenrJ   zipr   )rc   re   rf   valuereplacementr   r   r   r     s   
I
r   T)include_subgraphsr]   ._core.Graph | _core.GraphView | _core.Functionrn   dict[str, _core.Value]c                C  s   i }t | tjs|| j | jD ]}|jsq|j|v rq|||j< q|r+t| }n| }|D ]/}|jD ]}|s9q4|js=q4|j|v rCq4|||j< q4|j	D ]}|jsRqL|j|v rXqL|||j< qLq/|S )ay  Return a dictionary mapping names to values in the graph.

    The mapping includes values from subgraphs. Duplicated names are omitted,
    and the first value with that name is returned. Values with empty names
    are excluded from the mapping.

    .. versionchanged:: 0.1.2
        Values from subgraphs are now included in the mapping.

    .. versionadded:: 0.1.14
        The ``include_subgraphs`` parameter.

    Args:
        graph: The graph to extract the mapping from.
        include_subgraphs: If True, values from subgraphs are included in the mapping.

    Returns:
        A dictionary mapping names to values.
    )
r   r   FunctionupdateinitializersinputsrF   r   RecursiveGraphIteratoroutputs)r]   rn   rc   inputiteratornoderl   r   r   r   r   k  s:   





r   insertion_point
_core.Node	old_nodesSequence[_core.Node]	new_nodes
old_valuesSequence[_core.Value]
new_valuesgraph_or_function_core.Graph | _core.Functionc                C  s   t ||D ]4\}}|jdur|jn|j|_|jdur|jn|j|_|jdur)|jn|j|_|jdur5|jn|j|_qt||dd | || | j|dd dS )a  Replaces nodes and values in the graph or function.

    Args:
        graph_or_function: The graph or function to replace nodes and values in.
        insertion_point: The node to insert the new nodes after.
        old_nodes: The nodes to replace.
        new_nodes: The nodes to replace with.
        old_values: The values to replace.
        new_values: The values to replace with.
    NTri   )safe)rk   r7   shapeconst_valuerF   r   insert_afterremove)r   rz   r|   r~   r   r   	old_value	new_valuer   r   r   r     s   
r   rl   _core.Valuepropagate_shape_type _protocols.TensorProtocol | Nonec                 C  s  d}| j dur| j }n|  }|du rdS |jdks|jdkr!dS t|jdkr7td|j dt|j dt|jdkrMtd|j dt|j d	t	t
|j \}}| r^dS |jd
 }|dv rwtjtj|jtjd|jd}n=|dv rtjtj|jtjd|jd}n)|dv rtjtj|jtjd|jd}n|dkr| }ntd| d|j d| j|_|dur|r| jdur| j|jkrtd| | j|j |j| _t|j}| jdur| j|krtd| | j| || _|S )a7  Get the constant tensor from a value, if it exists.

    A constant tensor can be obtained if the value has a ``const_value`` set
    (as in the case of an initializer) or if the value is produced by a
    Constant node.

    This function will not alter the ``const_value`` of the value, but
    it will propagate the shape and type of the constant tensor to the value
    if `propagate_shape_type` is set to True.

    .. versionadded:: 0.1.2

    Args:
        value: The value to get the constant tensor from.
        propagate_shape_type: If True, the shape and type of the value will be
            propagated to the Value.

    Returns:
        The constant tensor if it exists, otherwise None.

    Raises:
        ValueError: If the Constant node does not have exactly one output or
            one attribute.
    NConstant    zConstant node 'z(' must have exactly one output, but has z	 outputs.z+' must have exactly one attribute, but has z attributes.r   >   value_floatvalue_floats)dtype)rF   >   	value_int
value_ints>   value_stringvalue_stringsrl   zUnsupported attribute 'z' in Constant node 'zy'. Expected one of 'value_float', 'value_floats', 'value_int', 'value_ints', 'value_string', 'value_strings', or 'value'.znValue '%s' has a shape %s that differs from the constant tensor's shape %s. The value's shape will be updated.zoValue '%s' has a type '%s' that differs from the constant tensor's type '%s'. The value's type will be updated.)r   producerop_typedomainrj   rv   rJ   rF   rb   nextiterra   is_refr   Tensornparrayrl   float32int64StringTensorbytes_	as_tensorr   r;   r<   r,   r   r7   )rl   r   tensorry   	attr_name
attr_valueir_valuenew_value_typer   r   r   r     sz   




r   )r   r   r   r   r   )rF   r"   r   r   rG   rH   r   rI   )r^   r_   r   r`   )F)rc   rd   re   rd   rf   rg   r   rh   )r]   ro   rn   rg   r   rp   )rz   r{   r|   r}   r~   r}   r   r   r   r   r   r   r   rh   )rl   r   r   rg   r   r   )(__doc__
__future__r   __all__loggingcollections.abcr	   r
   r   typingr   numpyr   r%   onnx_irr   r   r   r   r   r"   r   r!   r'   r&   r6   r+   r*   r/   r   	getLogger__name__r;   rE   r   r   r   r   r   r   r   r   r   r   <module>   sN   	

>
VWV
8(