class_docstr: Fold ``add``/``sub`` into ``bias`` of ``conv`` and ``conv_transpose``.
  That is, convert ``conv + add/sub`` to ``conv``, when ``add``/``sub`` is adding a constant.
  
  Two patterns are supported:
  
  .. code-block::
  
      Pattern 1:
      Given:
          %2 = conv(%1)
          ...
          %3 = add(%2, constant) # where constant has shape (1,C,1)/(C,1) for 1d conv, (1,C,1,1)/(C,1,1) for 2d conv etc
          ...
  
      Result:
          %3 = conv(%1)
          ...
  
  
      Pattern 2:
      Given:
          %2 = conv(%1)
          %3 = transpose(%2)
          ...
          %4 = add(%3, constant) # where constant has a broacasable shape
          ...
  
      Result:
          %2 = conv(%1)
          %4 = transpose(%2)
          ...
class_docstr_tok: ["Fold","``","add","``","\/","``","sub","``","into","``","bias","``","of","``","conv","``","and","``","conv_transpose","``",".","That","is",",","convert","``","conv","+","add\/sub","``","to","``","conv","``",",","when","``","add","``","\/","``","sub","``","is","adding","a","constant",".","Two","patterns","are","supported",":","..","code-block",":",":","Pattern","1",":","Given",":","%","2","=","conv","(","%","1",")","...","%","3","=","add","(","%","2",",","constant",")","#","where","constant","has","shape","(","1",",","C,1",")","\/","(","C,1",")","for","1d","conv",",","(","1",",","C,1,1",")","\/","(","C,1,1",")","for","2d","conv","etc","...","Result",":","%","3","=","conv","(","%","1",")","...","Pattern","2",":","Given",":","%","2","=","conv","(","%","1",")","%","3","=","transpose","(","%","2",")","...","%","4","=","add","(","%","3",",","constant",")","#","where","constant","has","a","broacasable","shape","...","Result",":","%","2","=","conv","(","%","1",")","%","4","=","transpose","(","%","2",")","..."]
code: def _try_to_transform(conv_op, add_op):
      if add_op.op_type == "sub":
          bias_var = add_op.y
      else:
          bias_var = add_op.x if add_op.x.val is not None else add_op.y
      bias_value = bias_var.val
  
      is_conv_op = conv_op.op_type == "conv"
  
      # check that the bias value is a constant array or a scalar constant
      if not isinstance(bias_value, (np.ndarray, np.generic)):
          return False
  
      is_bias_scalar = False
      if not isinstance(bias_value, np.ndarray):
          is_bias_scalar = True
  
      # find rank of the conv input
      rank = conv_op.x.rank
      if rank is None:
          return False
      if not (rank == 3 or rank == 4 or rank == 5):
          return False
  
      # check compatibility of bias value with the rank of the conv op
      # either bias value should be a scalar or:
      # rank=3 ==> (B,C,D), which means bias must be (1,C,1) or (C,1)
      # rank=4 ==> (B,C,D1,D2), which means bias must be (1,C,1,1) or (C,1,1)
      # rank=5 ==> (B,C,D1,D2,D3), which means bias must be (1,C,1,1,1) or (C,1,1,1)
  
      if is_bias_scalar:
          bias_value = np.array([bias_value])
      else:
          # check that there is at most one dimension in the shape that is not 1
          if len(np.squeeze(bias_value).shape) > 1:
              return False
          # check that addition is not happening on the batch dimension
          if len(bias_value.shape) == rank:
              if bias_value.shape[0] != 1:
                  return False
          # check that last rank-2 entries in the shape vector are all 1s
          if np.prod(bias_value.shape[-(rank - 2) :]) != 1:
              return False
          bias_value = np.squeeze(bias_value)
  
      if add_op.op_type == "sub":
          bias_value *= -1
  
      # everything looks good, now find the new updated bias
      old_bias = conv_op.inputs.get("bias", None)
      old_bias_value = None
      if old_bias is not None and old_bias.val is not None:
          old_bias_value = old_bias.val
      if old_bias is None:
          # need to create a fresh numpy array for bias
          if np.prod(bias_value.shape) == 1:
              # its a scalar bias
              # need to find the value of Cout to form a new bias
              if conv_op.weight.val is None:
                  return False
              # conv_transpose has weight format [K, C_out, spatial dims]
              # conv has weight format [C_out, K, spatial dims]
              Cout = conv_op.weight.val.shape[0 if is_conv_op else 1]
              new_bias_value = np.broadcast_to(bias_value, (Cout,))
          else:
              new_bias_value = bias_value
      else:
          # just need to update the existing bias array
          try:
              new_bias_value = old_bias_value + bias_value
          except:
              return False
  
      # create a new conv op with the new bias value, copying rest of the attributes
      out_name = add_op.outputs[0].name
      if (
          new_bias_value.dtype != np.float32
          and new_bias_value.dtype != np.float16
      ):
          # cast the bias to match the weight type
          weight_np_type = types.nptype_from_builtin(
              conv_op.inputs["weight"].sym_type.get_primitive()
          )
          logger.warning(
              "conv_bias_fusion pass: casting bias "
              "from {} to {} to match the dtype of the weight of the conv layer".format(
                  new_bias_value.dtype, weight_np_type
              )
          )
          new_bias_value = new_bias_value.astype(weight_np_type)
      new_bias_var = mb.const(val=new_bias_value, before_op=conv_op)
  
      conv_kargs = {
          "bias": new_bias_var,
          "name": out_name,
          "before_op": conv_op,
      }
  
      for k, v in conv_op.inputs.items():
          if k == "bias":
              continue
          conv_kargs[k] = v
  
      if is_conv_op:
          x = mb.conv(**conv_kargs)
      else:
          x = mb.conv_transpose(**conv_kargs)
  
      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([conv_op, add_op])
          return True
      return False
  
imports: import copy
  import numpy
  from coremltools import _logger
  from coremltools.converters.mil.mil import Block
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import Operation, types
  from coremltools.converters.mil.mil.passes.graph_pass import AbstractGraphPass
  from coremltools.converters.mil.mil.passes.helper import _check_child_op_type, _check_no_output_connection, block_context_manager
  from coremltools.converters.mil.mil.passes.pass_registry import register_pass
  from coremltools.converters.mil.mil.types.symbolic import any_symbolic
label_desc: Use image node_id 4 for calling the fuse_conv_bias obj's underlying member method code with example usage: obj._try_to_transform(conv_op, add_op) and returns: False, False, False, False, True, False, False, False, False, False