class_docstr: Convert ``matmul + add/sub`` to ``linear`` whenever possible.
  
  .. code-block::
  
      Given:
          %3 = matmul(x=%1, y=%2)  # %1 or %2 is const and rank 2 (weight)
          ...
          %5 = add(x=%3, y=%4) # %4 is const. add(x=%4, y=%3) is equivalent
                               # sub is similar.
  
      Result:
          # assuming %2 above is const and rank 2
          %5 = linear(x=%1, weight=%2, bias=%4)
class_docstr_tok: ["Convert","``","matmul","+","add\/sub","``","to","``","linear","``","whenever","possible",".","..","code-block",":",":","Given",":","%","3","=","matmul","(","x=","%","1",",","y=","%","2",")","#","%","1","or","%","2","is","const","and","rank","2","(","weight",")","...","%","5","=","add","(","x=","%","3",",","y=","%","4",")","#","%","4","is","const",".","add","(","x=","%","4",",","y=","%","3",")","is","equivalent","#","sub","is","similar",".","Result",":","#","assuming","%","2","above","is","const","and","rank","2","%","5","=","linear","(","x=","%","1",",","weight=","%","2",",","bias=","%","4",")"]
code: def _try_to_transform(self, matmul_op, add_op, block):
      if matmul_op.x.val is None and matmul_op.y.val is None:
          # This is a dynamic matmul.
          return False
      if add_op.x.val is None and add_op.y.val is None:
          # This is a dynamic add.
          return False
  
      x_is_weight = matmul_op.x.val is not None
      if x_is_weight:
          weight, linear_x = matmul_op.x, matmul_op.y
          transpose_weight = matmul_op.transpose_x.val
          transpose_x = matmul_op.transpose_y.val
      else:
          weight, linear_x = matmul_op.y, matmul_op.x
          transpose_weight = matmul_op.transpose_y.val
          transpose_x = matmul_op.transpose_x.val
  
      # We potentially are going to transpose the weight, so if the weight itself is not removable, we skip this path
      if len(weight.nonreplaceable_vars_upstream) > 0:
          return False
  
      if linear_x.rank < 2 or weight.rank != 2:
          # We don't support these cases yet.
          return False
  
      # For those weights which are the input for more than one op,
      # we don't do the fusion.
      # The reason is that it might cause memory explosion by adding
      # those weight as a numpy array in the inner product or
      # the batch_mat_mul kernel.
      if len(weight.child_ops) > 1:
          return False
  
      d_out = (
          weight.shape[1] if not transpose_weight else weight.shape[0]
      )
      bias = add_op.x.val if add_op.x.val is not None else add_op.y.val
      if len(bias.shape) > 1:
          if any([d != 1 for d in bias.shape[:-1]]):
              return  # cannot transform
  
          # squeeze leading dims of size 1
          bias = np.squeeze(bias)
  
      if len(bias.shape) != 1 or bias.shape[0] != d_out:
          return  # cannot transform
  
      if add_op.op_type == "sub":
          bias = -bias
      out_name = add_op.outputs[0].name
  
      if x_is_weight:
          # If transpose_x == transpose_weight == False:
          # w*x = (x^T w^T)^T = linear(x^T, w)^T
          x_transposed = (
              self._transpose(linear_x, before_op=matmul_op)
              if not transpose_x
              else linear_x
          )
          w_no_transpose = (
              weight
              if not transpose_weight
              else self._transpose(weight, before_op=matmul_op)
          )
          x = mb.linear(
              x=x_transposed,
              weight=w_no_transpose,
              bias=bias,
              before_op=matmul_op,
          )
          x = self._transpose(x, before_op=matmul_op, name=out_name)
      else:
          # If transpose_x == transpose_weight == False
          # x*w = x*(w^T)^T = linear(x, w^T)
          x_no_transpose = (
              self._transpose(linear_x, before_op=matmul_op)
              if transpose_x
              else linear_x
          )
          w_transposed = (
              weight
              if transpose_weight
              else self._transpose(weight, before_op=matmul_op)
          )
          x = mb.linear(
              x=x_no_transpose,
              weight=w_transposed,
              bias=bias,
              before_op=matmul_op,
              name=out_name,
          )
  
      if add_op.enclosing_block.try_replace_uses_of_var_after_op(
          anchor_op=add_op,
          old_var=add_op.outputs[0],
          new_var=x,
      ):
          add_op.enclosing_block.remove_ops([matmul_op, add_op])
          return True
      return False
  
file_path: coremltools/coremltools/converters/mil/mil/passes/defs/optimize_linear.py
imports: import numpy
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import Program
  from coremltools.converters.mil.mil.passes.graph_pass import AbstractGraphPass
  from coremltools.converters.mil.mil.passes.helper import block_context_manager
  from coremltools.converters.mil.mil.passes.pass_registry import register_pass
label_desc: Use image node_id 4 for calling the fuse_matmul_weight_bias obj's underlying member method code with example usage: obj._try_to_transform(matmul_op, add_op, block) and returns: False, False, False, False, False, False, True