
    g{                    L   d dl mZ d dlZd dlZd dlZd dlZd dlZd dlmZ d dl	m
Z
 d dlZddlmZmZmZmZ  G d d      Z G d	 d
      Z	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 ddZddZddZdddZddZdd dZd Zd Zedk(  r e        yy)!    )annotationsN)deque)IntEnum   )ModelProtoWithShapeInfoget_producer_consumer_mapsis_fixed_size_tensoroptimize_modelc                  "    e Zd ZdZd Zd Zd Zy)_SupportedOpsCheckera;  
    Class to process the md file with list of supported ops and caveats for an execution provider.
    e.g. /tools/ci_build/github/android/nnapi_supported_ops.md
         /tools/ci_build/github/apple/coreml_supported_mlprogram_ops.md
         /tools/ci_build/github/apple/coreml_supported_neuralnetwork_ops.md
    c                n   || _         i | _        t               | _        t	        |      5 }|D ]r  }|j                  d      s|j                         j                  d      }t        |      dk(  sC|d   }|d   }|j                  dd      }d|v sd|| j                  |<   t 	 d d d        y # 1 sw Y   y xY w)N|      r   z<br/> :)
	_filename_opsset	_ops_seenopen
startswithstripsplitlenreplace)selffilenameflinepieces	domain_opcaveats          g/var/www/openai/venv/lib/python3.12/site-packages/onnxruntime/tools/mobile_helpers/usability_checker.py__init__z_SupportedOpsChecker.__init__   s    !	(^q ??3'!ZZ\//4F6{a'$*1I	!'!'!= )+39DIIi0  ^^s   B+-B+/ B+B++B4c                    |j                   r|j                   nd}|dz   |j                  z   }|| j                  v }|r| j                  j	                  |       |S )Nai.onnxr   )domainop_typer   r   add)r   noder(   r"   is_supporteds        r$   is_op_supportedz$_SupportedOpsChecker.is_op_supported.   sN     $SL4<</	 DII-NNy)    c                    g }t        | j                        D ]*  }| j                  |   }|s|j                  | d|        , |S )Nr   )sortedr   r   append)r   caveatsopr#   s       r$   get_caveatsz _SupportedOpsChecker.get_caveats8   sH    (BYYr]F"Qvh/0 )
 r.   N)__name__
__module____qualname____doc__r%   r-   r4    r.   r$   r   r      s    :(r.   r   c                  h    e Zd Z G d de      Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZd	dZd Zd
dZy)PartitioningInfoc                      e Zd ZdZdZdZy)PartitioningInfo.TryWithEP)r   )r   r   N)r5   r6   r7   NOMAYBEYESr9   r.   r$   	TryWithEPr=   C   s    r.   rA   c                    || _         || _        || _        || _        || _        || _        || _        || _        |	| _        |
| _	        d| _
        d| _        y )Nr   )	num_nodesnum_supported_nodesnum_partitionssupported_ops_checkersupported_groupsunsupported_opsnodes_unsupported_due_to_op&nodes_unsupported_due_to_dynamic_input!num_unsupported_nodes_due_to_rankops_with_unsupported_ranknum_subgraphsnum_nodes_in_subgraphs)r   rC   rD   rE   rF   rG   rH   rI   rJ   rK   rL   s              r$   r%   zPartitioningInfo.__init__H   sc     ##6 ,%:" 0.+F(6\31R.)B&&'#r.   c                   | xj                   |j                   z  c_         | xj                  |j                  z  c_        | xj                  |j                  z  c_        | j                  j	                  |j                         | j
                  j                  |j
                         | xj                  |j                  z  c_        | xj                  |j                  z  c_        | xj                  |j                  z  c_	        | j                  j                  |j                         | xj                  dz  c_        | xj                  |j                   z  c_        y)z]
        Merge the information from another PartitioningInfo instance into this one.
        r   N)rC   rD   rE   rG   extendrH   updaterI   rJ   rK   rL   rM   rN   )r   others     r$   mergezPartitioningInfo.mergec   s     	%//)  E$=$== u333$$U%;%;<##E$9$9:((E,M,MM(33u7c7cc3..%2Y2YY.&&--e.M.MN 	a##u6#r.   c                   | j                   | j                  z  dz  }| j                  dk(  rX|dkD  rt        j                  j
                  S |dkD  rt        j                  j                  S t        j                  j                  S | j                  dk(  r9|dkD  rt        j                  j                  S t        j                  j                  S t        j                  j                  S )Nd   r   K   2   r   )rD   rC   rE   r;   rA   r@   r?   r>   )r   pct_supporteds     r$   suitabilityzPartitioningInfo.suitabilityu   s     004>>ACG!#r!'11555#'11777'11444!#r!'11777'11444)),,,r.   c                   |j                  | j                   d| j                   d| j                   d| d       | j                  r|j                  ddj                  | j                  D cg c]  }t        t        |             c}       d       | j                  D ]L  }|j                  dd	j                  |D cg c]  }|j                   d
|j                    c}              N |j                  d| j                          | j                  r6|j                  dd	j                  t        | j                                      | j                  j                         }|rGd}|j                  ddj                  |D cg c]  }t         j"                  |z   |z    c}              | j$                  r|j                  d| j$                         | j&                  rT|j                  d| j&                          |j                  dd	j                  t        | j(                                      | j*                  dkD  r+|j                  | j,                   d| j*                   d       | j                  | j                  z  dz  }	| j                  dk(  r|j                  | d       y)| j                  dk(  rU|	dkD  r|j                  | d|	dd       y)|	dkD  r|j                  | d|	dd       y)|j                  | d |	d!d"       y)| j                  d#k(  r|	dkD  r|j                  | d$|	dd%       y)|j                  | d&| j                   d'|	dd(       y)c c}w c c}w c c}w )*z
        Analyze the partitioning information and log the analysis
        :param logger: Logger to use
        :param ep_name: Execution provider name to use in the log messages
        z partitions with a total of /z nodes can be handled by the z EP.z	Partition sizes: [z, ]zNodes in group: ,r   z"Unsupported nodes due to operator=z	Unsupported ops: z     z\	Caveats that have not been checked and may result in a node not actually being supported:   z8Unsupported nodes due to input having a dynamic shape=%dz,Unsupported nodes due to rank of input data=z	Ops with unsupported rank: r   z nodes are in z; subgraphs. Check EP as to whether subgraphs are supported.rU   z$ cannot run any nodes in this model.r   rV   zD should work well for this model as there is one partition covering z.1fz% of the nodes in the model.rW   z, may work well for this model, however only zD% of nodes will use it. Performance testing is required to validate.z4 will probably not work will for this model as only z.2fz% of nodes will use it.r   zG can be considered for this model as there are two partitions covering z<% of the nodes. Performance testing is required to validate.z1 is not recommended with this model as there are z partitions covering zj% of the nodes in the model. This will most likely result in worse performance than just using the CPU EP.N)inforE   rD   rC   rG   joinstrr   debugr)   namerI   rH   r0   rF   r4   oslineseprJ   rK   rL   rM   rN   )
r   loggerep_name	partitiongroupr+   r2   indentr#   pct_nodes_using_eps
             r$   print_analysiszPartitioningInfo.print_analysis   s    	""##?@X@X?YYZ[_[i[iZj k++2)49	

   KK&tyyW[WlWl1mWl)#c)n2EWl1m'n&oopq
 ../di9jdi\`T\\N!DII;:Wdi9j0k/lmn / 	89Y9Y8Z[\KK-chhvd>R>R7S.T-UVW,,88:FKKo77wOwVBJJ/&8wOPQS
 66KKJ;;
 11KKFtGmGmFnopKK7HfHfAg8h7ijk! KK../~d>P>P=Q RB B
 "55FL!#KK7)#GHI  A%!B&i    2377SU $b(iKL^_bKc dP P
 iSTfgjSk l, ,
   A%*<r*AKK) .s3 4?? KK)LTM`M`La b.s3 4``Q 2n
 :k Ps   -M- "M2
M7N)rC   intrD   rm   rE   rm   rF   r   rG   zlist[onnx.NodeProto]rH   set[str]rI   rm   rJ   rm   rK   rm   rL   rn   )rR   r;   )rf   logging.Loggerrg   ra   )	r5   r6   r7   r   rA   r%   rS   rY   rl   r9   r.   r$   r;   r;   B   s    G 
(( !( 	(
  4( /( "( &)( 14( ,/( $,(67$-.Zr.   r;   c                   !"#$% | j                   D cg c]  }|j                   c}!!fd d "g %i }	t               }
t               }| j                  D ]2  }||v rt	        ||         nd}||	|<   |dk(  s"|
j                  |       4 g #t               $d}d}d}d}t               }t               }"#$%fd}|
s|r:|
s |        |}
t               }|
j                         }|j                  |      }| xs t         fd|j                  D              }d}rw|j                  D ]h  }|s|v s|   j                  j                  d      s*t	        |   j                  j                  j                  j                        }||kD  sfd} n |j                   dk(  r't	        |j"                  d   j$                        |kD  rd}|xr |xr |}|s|$v r|j                  |       3|s=|j'                  |j(                  r|j(                  nd	 d
|j                           |dz  }|s|dz  }|s=|dz  }|j'                  |j(                  r|j(                  nd	 d
|j                           |rJ|dz  }#j                  |       |$v r$j+                  |       ||v r||   D ]  }$j'                  |        ||v r/||   D ]'  }|	|   }|dz  }|dk(  r|
j                  |       ||	|<   ) |
r6|r: |        t	        | j                        }t	        %      }t-        ||||%|||||
      }|S c c}w )Nc                <    | v rt        |          S | v s| v ryy)NTF)r	   )valueinitializersouter_scope_initializers
value_infos    r$   _is_fixed_shape_valuez<_check_partitioning_for_graph.<locals>._is_fixed_shape_value   s3    J'
5(9::L E-E$E r.   r   c                     rO xs        } | rj                  j                                j                          j                          y y N)r1   copyclear)keep_partitionon_group_closed_fnsupported_groupsupported_group_borderrG   s    r$   close_groupz2_check_partitioning_for_graph.<locals>.close_group*  sP    !33Z7I/7ZN ''(<(<(>?!!#"((* r.   c              3  .   K   | ]  } |        y wrx   r9   ).0irv   s     r$   	<genexpr>z0_check_partitioning_for_graph.<locals>.<genexpr>>  s     GujtefH]^_H`jts   Ttensor_typeF	Transposer'   r   r   )initializerrc   r   r+   r   r1   r   popleftr-   allinputtypeHasFieldr   shapedimr)   	attributeintsr*   r(   remover;   )&graphnode_to_producersnode_to_consumersrF   rt   require_fixed_input_sizesru   max_rankr   	in_degreenodes_to_process nodes_to_process_with_next_groupr+   node_input_edge_countrD   num_unsupported_nodes_due_to_op*num_unsupported_nodes_due_to_dynamic_inputrK   rH   rL   r   r-   is_input_shape_supportedis_rank_supported
node_input
input_rankis_node_supportedconsumerconsumer_node_in_degreerC   rE   r_   rv   rs   r|   r}   r~   rG   s&       ` `                         @@@@@@r$   _check_partitioning_for_graphr      s    %*$5$56$5qAFF$56L	0 Iw',w$ 

@DHY@Y$5d$; <_`/	$ A%##D)  O U&'#12.()%eO #+ >M?/4w,'')/??E'@#@#uCGujnjtjtGuDu  "jj
*
":z*?U?Z?Z?c?cdq?r!$Z
%;%@%@%L%L%R%R%V%V!WJ!H,,1) ) <<;&3t~~a/@/E/E+F+Q %+^0H^M^ -- 177="##dkkt{{y&QQRSWS_S_R`$ab/14/+:a?:$1Q61)--QZ0[[\]a]i]i\j.kl1$ ""4( --&--d3 (( 1$ 7H*..x8 !8 $$-d3*3H*=''1,'*a/$++H5&=	(# 4 >N MEJJI)*N'2)!D Ks 7s   L;c           	        r;t        | j                        dk(  r#t        | j                        dkD  rt        d      ddt	        |       \  	 	 d	 	 	 	 	 	 	 	 	 d	fd | ri       }|S d      }|S )
as  
    Estimate the partitions the graph will be split into for nodes that is_node_supported_fn returns true for.

    The check on whether a node is supported is purely based on the operator type. Additional limitations
    (e.g. NNAPI EP only supports 2D Conv) are not checked, so partitions may not be 100% accurate. The limitations
    for operators in the partitions are printed so the user can manually check.
    :param main_graph: Graph to process
    :param supported_ops_checker: Checker with info on supported ops.
    :param require_fixed_input_sizes: If True, require that the inputs to a potentially supported node are fixed size
                                      tensors for it to be considered as supported. This requires
                                      onnx.shape_inference.infer_shapes to have been run on the model to populate the
                                      shape information.
                                      If False, shapes are ignored during the check.
    :param max_rank: Set if EP has a limitation on the rank of tensors it supports.
    :return PartitioningInfo instance with details
    r   r   zURun onnx.shape_inference.infer_shapes on the model to populate the shape information.c                    | j                   D ]  }|||j                  <    | j                  D ]  }|||j                  <    | j                  D ]  }|||j                  <    y rx   )r   rc   outputru   )r   value_to_shapevs      r$   _update_value_infoz.check_partitioning.<locals>._update_value_info  sX    A%&N166" A%&N166" !!A%&N166" "r.   Nc           
        ||j                         } | |       ni }|
t               }t        | ||      }|r|j                  |       n|}t        |      }| j                  D ]  }|j                  |j                          | j                  D ]<  }|j                  D ]+  }	|	j                  d      s|	j                  }
 |
|||      }- > |S )Ng)ry   r   r   rS   r   r*   rc   r+   r   r   r   )r   outer_scope_value_infort   partitioning_inforu   r_   !subgraph_outer_scope_initializersr   r+   attrsubgraph_check_graphr   r   r   r   r   rF   s              r$   r   z(check_partitioning.<locals>._check_graph  s     "-/446Juj1J#+'*u$,!$%	
 ##D) !% -00H,I) ,,K-11+2B2BC - JJD==%#vvH(4 *.OQb)% '  ! r.   )r   onnx.GraphProtor   dict[str, onnx.ValueInfoProto])NN)
r   r   r   z%dict[str, onnx.ValueInfoProto] | Nonert   zset[str] | Noner   zPartitioningInfo | Nonereturnr;   )r   ru   r+   
ValueErrorr   )	
main_graphrF   r   r   aggregated_partitioning_infor   r   r   r   s	    ``` @@@@r$   check_partitioningr     s    . !S)>)>%?1%DZ__I]`aIapqq' ,Fj+Q((
 5959	1!1! E1! #21! 3	1!
 
1! 1!f $0
B[B#f '' bf#f ''r.   c                L    t        |      }t        | j                  |||      }|S rx   )r   r   r   )modelsupported_ops_configr   r   supported_opspartition_infos         r$   _check_ep_partitioningr     s+     ))=>M']D]_ghNr.   c                    t        j                  t              j                  }|dz  }|j	                         r|}n |j
                  d   }|dz  dz  dz  dz  dz  }t        | ||      S )Nznnapi_supported_ops.md   toolsci_buildgithubandroidpathlibPath__file__parentexistsparentsr   )r   r   
script_dirlocal_configconfig_pathort_roots         r$   check_nnapi_partitionsr     ss     h'..J 88L"%%a((:5@9LOgg!%6OPPr.   c                    t        j                  t              j                  }||z  }|j	                         r|}n |j
                  d   }|dz  dz  dz  dz  |z  }d}t        | |||      S )Nr   r   r   r   apple   r   )r   r   config_filenamer   r   r   r   r   s           r$   check_coreml_partitionsr     sy     h'..J/L"%%a((:5@7J_\H!%6OQYZZr.   c           	     j   d}d}g }| j                   D ]e  }t        |      sS|j                  |       |r:|j                  ddj	                  t        |      j                                       |dz  }a|dz  }g g }| j                  D ]e  }t        |      sS|j                  |       |r:|j                  ddj	                  t        |      j                                       |dz  }a|dz  }g | j                  sJt        | j                        dk(  s2t        |      t        | j                         k(  s|j                  d       | j                  D ]  }t        |      r|dz  }|dz  } |rA|j                  d| d|        |r(|r|j                  d	       ||fS |j                  d
       ||fS )a  
    Check the shapes of graph inputs, values and graph outputs to determine if they have static or dynamic sizes.
    NNAPI does not support dynamically sized values. CoreML does, but it will most likely cost performance.
    :param graph: Graph to check. If shape inferencing has been run the checks on values will be meaningful.
    :param logger: Optional logger for diagnostic information.
    :return: Tuple of List of inputs with dynamic shapes, Number of dynamic values found
    r   z"Input is not a fixed size tensor: r   r   z#Output is not a fixed size tensor: ziUnable to check shapes within model. ONNX shape inferencing should be run on the model prior to checking.zNum values with fixed shape=z . Num values with dynamic shape=zModel has dynamic inputs and outputs. Consider re-exporting model with fixed sizes if NNAPI or CoreML can be used with this model.ao  Model has dynamically sized inputs but fixed sized outputs.
                       If the sizes become fixed early in the model (e.g. pre-processing of a dynamic input size
                       results in a fixed input size for the majority of the model) performance with NNAPI and CoreML,
                       if applicable, should not be significantly impacted.)r   r	   r1   r_   r`   ra   r   r   ru   r   r+   warning)	r   rf   num_fixed_valuesnum_dynamic_valuesdynamic_inputsr   dynamic_outputsovis	            r$   check_shapesr     s    N[[#A&!!!$@#a&,,.AY@Z[\!#!  O\\#A&""1%A#((3q6<<>BZA[\]!#!  S_%9S=PTWX]XcXcTd=dS	

 #!!#	  *+;*<<\]o\pq	
 F --- O ---r.   c                  
 t        |       }|j                  t        j                        \  
}
fd} |dt              }dd}dd} |d|      } |d|      }	|t
        j                  j                  k7  s:|t
        j                  j                  k7  s|	t
        j                  j                  k7  r2j                         t        j                  kD  rj                  d       |t
        j                  j                  k7  xs< |t
        j                  j                  k7  xs |	t
        j                  j                  k7  S )	Nc                f   j                  d|         d} |	|      }j                         t        j                  k  r|j	                  |        |j                         }j                  d|  d|j                          |t        j                  j                  k7  rrj                  d       j                  d       d} |	|      }j                         t        j                  k  r#j                  d       |j	                  |        |j                         }j                  d|  d	|j                          |t        j                  j                  k7  rj                  d
       |j                  |j                  kD  r|}j                  d       j                  d       |S )N	Checking TzModel should perform well with z as is: z--------zHChecking if model will perform better if the dynamic shapes are fixed...FzHPartition information if the model was updated to make the shapes fixed:z) if modified to have fixed input shapes: zPShapes can be altered using python -m onnxruntime.tools.make_dynamic_shape_fixedz================r^   )r_   getEffectiveLevelloggingINFOrl   rY   rc   r;   rA   r@   r>   rr   )
rg   checker_funcr   r   rY    partition_info_with_fixed_shapesfixed_shape_suitabilityr   rf   model_with_shape_infos
          r$   check_epzchecker.<locals>.check_epb  s   iy)* %)!%&;=VW##%5))&':$0025gYh{GWGWFXYZ*44888^KK
#KKbc(-%/;<QSl/m,'')W\\9fg0??P&F&R&R&T#KK1':c*//02
 '*:*D*D*G*GGno&,,{/@/@@5&'Br.   NNAPIc                    t        | |d      S )Nz%coreml_supported_neuralnetwork_ops.mdr   r   r   s     r$   check_nn_coremlz checker.<locals>.check_nn_coreml  s    &u.GIpqqr.   c                    t        | |d      S )Nz!coreml_supported_mlprogram_ops.mdr   r   s     r$   check_mlprogram_coremlz'checker.<locals>.check_mlprogram_coreml  s    &u.GIlmmr.   zCoreML NeuralNetworkzCoreML MLProgramzJRe-run with log level of INFO for more details on the NNAPI/CoreML issues.)r   onnx.ModelProto)r   r   r   r   r   r;   rA   r@   r   r   r   r_   r>   )
model_pathrf   model_with_shape_info_wrapperr   r   nnapi_suitabilityr   r   coreml_nn_suitabilitycoreml_mlprogram_suitabilityr   r   s    `        @@r$   checkerr   \  s'   $;J$G!9OO)56K6Q6Q)R&N&&P !*@Arn %%;_M#+,>@V#W  	-77;;; $4$>$>$B$BB'+;+E+E+I+II

"
"
$w||
3`a 	-77::: 	I $4$>$>$A$AA	I'+;+E+E+H+HHr.   c                   |s4t        j                  d      }|j                  t         j                         |j	                  d|  d       t        j                         5 }|s2t        j                  |      | j                  z  }t        | |d       |} t        | j                  d      |      }ddd       |S # 1 sw Y   S xY w)a  
    Analyze the provided model to determine if it's likely to work well with the NNAPI or CoreML Execution Providers
    :param model_path: Model to analyze.
    :param skip_optimize: Skip optimizing to BASIC level before checking. When exporting to ORT format we will do this
                          optimization..
    :param logger: Logger for output
    :return: True if either the NNAPI or CoreML Execution Providers may work well with this model.
    usability_checkerr   z for usability with ORT Mobile.T)use_external_initializers)strictN)r   	getLoggersetLevelr   r_   tempfileTemporaryDirectoryr   r   rc   r
   r   resolve)r   skip_optimizerf   tmptmp_pathtry_epss         r$   analyze_modelr    s     ""#67%
KK)J<'FGH		$	$	&#||C(:??:H:x4P!J*,,D,96B 
' N 
' Ns    AB;;Cc                 ,   t        j                  t        j                  j	                  t
              d      } | j                  dddgdd       | j                  dd	d
       | j                  dt        j                  d       | j                         S )Nz3Analyze an ONNX model for usage with the ORT mobile)descriptionz--log_levelrb   r_   zLogging level)choicesdefaulthelpz--skip_optimize
store_truezDon't optimize the model to BASIC level prior to analyzing. Optimization will occur when exporting the model to ORT format, so in general should not be skipped unless you have a specific reason to do so.)actionr
  r   zProvide path to ONNX model)r   r
  )
argparseArgumentParserrd   pathbasenamer   add_argumentr   r   
parse_args)parsers    r$   r  r    s    $$
"0iF /@&Wfg
L   7<<>Z[r.   c                    t               } t        j                  d      }| j                  dk(  r |j	                  t        j
                         n}| j                  dk(  r |j	                  t        j                         nN| j                  dk(  r |j	                  t        j                         n|j	                  t        j                         | j                  j                         }t        || j                  |       y )Nr	  rb   r_   r   )r  r   r   	log_levelr   DEBUGr   WARNINGERRORr   r   r  r  )argsrf   r   s      r$   run_analyze_modelr    s    <Dy)F~~ &	6	!%	9	$(&((*J*d00&9r.   __main__)i  )r   r   r   )dict[onnx.NodeProto, set[onnx.NodeProto]]r   r  rF   r   rt   rn   r   boolru   r   r   rm   )
r   r   rF   r   r   r  r   rm   r   r;   )r   r   r   pathlib.Pathr   r  r   rm   )r   r  )r   r   r   r  r   ra   rx   )r   r   rf   logging.Logger | None)r   r  rf   ro   )FN)r   r  r  r  rf   r  )
__future__r   r  r   rd   r   r   collectionsr   enumr   onnxonnx_model_utilsr   r   r	   r
   r   r;   r   r   r   r   r   r   r   r  r  r  r5   r9   r.   r$   <module>r%     sJ   #   	      x x- -`d d^ dd@d Ad 0	d
 'd  $d /d dV 	[([(/[(  $[( 	[(
 [(~ ru2>[_knQ[F.RFR4$:" z r.   