class_docstr: Convert ``linear + add/sub`` to a single ``linear`` by updating the weight and bias of the ``linear`` layer.
  
  .. code-block::
  
      Example 1:
          Original:
              %4 = linear(x=%1, weight=%2, bias=%3) # %2 is a rank-2 const tensor (weight)
                                                    # %3 is a rank-1 const tensor (bias)
              ...
              %6 = add(x=%4, y=%5) # %5 is a const tensor with same shape as %3
  
          Result:
              %8 = linear(x=%1, weight=%2, bias=%7) # where %7 is a new const tensor with value
                                                    # %7 = %3 + %6
  
      Example 2:
          Original:
              %4 = linear(x=%1, weight=%2, bias=%3) # %2 is a rank-2 const tensor (weight)
                                                    # %3 is a rank-1 const tensor (bias)
              ...
              %6 = sub(x=%5, y=%4) # %5 is a const tensor with a broacasable shape with %3.
                                     i.e. if %3 has shape (Dout), %5 could be (1, Dout).
  
          Result:
              %9 = linear(x=%1, weight=%7, bias=%8) # where %7 is a new const tensor with value %7 = -%2
                                                    # %8 = %5 - %3
class_docstr_tok: ["Convert","``","linear","+","add\/sub","``","to","a","single","``","linear","``","by","updating","the","weight","and","bias","of","the","``","linear","``","layer",".","..","code-block",":",":","Example","1",":","Original",":","%","4","=","linear","(","x=","%","1",",","weight=","%","2",",","bias=","%","3",")","#","%","2","is","a","rank-2","const","tensor","(","weight",")","#","%","3","is","a","rank-1","const","tensor","(","bias",")","...","%","6","=","add","(","x=","%","4",",","y=","%","5",")","#","%","5","is","a","const","tensor","with","same","shape","as","%","3","Result",":","%","8","=","linear","(","x=","%","1",",","weight=","%","2",",","bias=","%","7",")","#","where","%","7","is","a","new","const","tensor","with","value","#","%","7","=","%","3","+","%","6","Example","2",":","Original",":","%","4","=","linear","(","x=","%","1",",","weight=","%","2",",","bias=","%","3",")","#","%","2","is","a","rank-2","const","tensor","(","weight",")","#","%","3","is","a","rank-1","const","tensor","(","bias",")","...","%","6","=","sub","(","x=","%","5",",","y=","%","4",")","#","%","5","is","a","const","tensor","with","a","broacasable","shape","with","%","3",".","i.e",".","if","%","3","has","shape","(","Dout",")",",","%","5","could","be","(","1",",","Dout",")",".","Result",":","%","9","=","linear","(","x=","%","1",",","weight=","%","7",",","bias=","%","8",")","#","where","%","7","is","a","new","const","tensor","with","value","%","7","=","-","%","2","#","%","8","=","%","5","-","%","3"]
code: def _try_to_transform(linear_op, add_or_sub_op, block):
      if add_or_sub_op.x.val is None and add_or_sub_op.y.val is None:
          return False
  
      is_sub = add_or_sub_op.op_type == "sub"
      is_first_input = add_or_sub_op.x == linear_op.outputs[0]
  
      # Return if weight or bias are missing values
      if linear_op.weight.val is None or linear_op.bias.val is None:
          return False
  
      # compute the new bias
      linear_bias = linear_op.bias.val
      bias = (
          add_or_sub_op.y.val if is_first_input else add_or_sub_op.x.val
      )
  
      # check if the shape is broadcasable
      if np.prod(linear_bias.shape) != np.prod(bias.shape):
          return False
      Dout = linear_bias.shape[0]
      if bias.shape[-1] != Dout:
          return False
      bias = np.reshape(bias, (Dout,))
  
      if is_sub:
          if is_first_input:
              bias = -bias
          else:
              linear_bias = -linear_bias
  
      new_bias = linear_bias + bias
  
      # compute the new weight
      if is_sub and not is_first_input:
          new_weight = -linear_op.weight.val
      else:
          new_weight = linear_op.weight.val
  
      # create a new linear op with the new weight, bias value, copying rest of the attributes
      out_name = add_or_sub_op.outputs[0].name
      linear_kargs = {
          "weight": new_weight,
          "bias": new_bias,
          "name": out_name,
          "before_op": linear_op,
      }
  
      for k, v in linear_op.inputs.items():
          if k in ["weight", "bias"]:
              continue
          linear_kargs[k] = v
  
      x = mb.linear(**linear_kargs)
  
      if add_or_sub_op.enclosing_block.try_replace_uses_of_var_after_op(
          anchor_op=add_or_sub_op,
          old_var=add_or_sub_op.outputs[0],
          new_var=x,
      ):
          add_or_sub_op.enclosing_block.remove_ops(
              [linear_op, add_or_sub_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 2 for calling the fuse_linear_bias obj's underlying member method code with example usage: obj._try_to_transform(linear_op, add_or_sub_op, block) and returns: False, False, False, False, False, True