class_docstr: Fuse the following ``batch_norm`` layer into ``conv`` and ``conv_transpose``.
  That is, convert ``conv + batch_norm`` to ``conv``, by modifying the weight and bias in the ``conv`` layer.
  
  .. code-block::
  
      Given:
          %2 = conv(%1)
          ...
          %3 = batch_norm(%2)
          ...
  
      Result:
          %3 = conv(%1)
          ...
class_docstr_tok: ["Fuse","the","following","``","batch_norm","``","layer","into","``","conv","``","and","``","conv_transpose","``",".","That","is",",","convert","``","conv","+","batch_norm","``","to","``","conv","``",",","by","modifying","the","weight","and","bias","in","the","``","conv","``","layer",".","..","code-block",":",":","Given",":","%","2","=","conv","(","%","1",")","...","%","3","=","batch_norm","(","%","2",")","...","Result",":","%","3","=","conv","(","%","1",")","..."]
code: def _try_to_transform(conv_op, bn_op):
      # get parameters from batch_norm layer
      gamma = bn_op.gamma.val
      beta = bn_op.beta.val
      mean = bn_op.mean.val
      variance = bn_op.variance.val
      epsilon = bn_op.epsilon.val
  
      # get weight, bias and groups from conv layer
      if conv_op.weight.val is None:
          return False
      conv_weight = conv_op.weight.val
      conv_bias = conv_op.bias
      groups = conv_op.groups.val
  
      # get type of the conv layer
      is_deconv = conv_op.op_type == "conv_transpose"
      # The deconv weight transpose axes is determined by the dimension of convolution.
      # Conv1d should be [1, 0, 2], Conv2d should be [1, 0, 2, 3], Conv3d should be [1, 0, 2, 3, 4]
      if not 3 <= len(conv_weight.shape) <= 5:
          raise AssertionError(
              f"Only supports Conv1/2/3d, which means weight's dimension should"
              f"between 3 and 5, but got weight with {len(conv_weight.shape)} "
              f"dimensions. "
          )
      deconv_weight_transpose_axes = [1, 0] + [
          axis for axis in range(2, len(conv_weight.shape))
      ]
  
      # D_in denotes the spatial dimensions for conv kernel weight
      # for conv_transpose, conv_weight has shape [Cin, Cout / groups, *D_in]
      # for conv, conv_weight has shape [Cout, Cin / groups, *D_in]
      if is_deconv:
          Cout = conv_weight.shape[1] * groups
          Cin = conv_weight.shape[0]
      else:
          Cout = conv_weight.shape[0]
          Cin = conv_weight.shape[1] * groups
  
      # get the type of the conv weight
      conv_weight_type = conv_weight.dtype
  
      # create bias for conv if not exist
      if conv_bias is None:
          conv_bias = np.zeros(Cout)
      else:
          conv_bias = conv_bias.val
  
      if conv_bias is None:
          return False
  
      conv_bias = conv_bias.astype(conv_weight_type)
  
      # get the original shape of weight and bias
      origin_weight_shape = conv_weight.shape
      origin_bias_shape = conv_bias.shape
  
      # update the weight for conv layer
      new_conv_weight = []
      new_conv_bias = []
  
      if is_deconv:
          conv_weight = np.transpose(
              conv_weight, deconv_weight_transpose_axes
          )
          conv_weight = np.reshape(
              conv_weight,
              [Cout, Cin // groups] + list(conv_weight.shape[2:]),
          )
  
      for i in range(Cout):
          # get batch norm parameters for each channel
          _gamma = gamma[i]
          _beta = beta[i]
          _mean = mean[i]
          _variance = variance[i]
          _scale = _gamma / np.sqrt(_variance + epsilon)
  
          # get conv weight and bias for each channel
          _conv_weight = conv_weight[i]
          _conv_bias = conv_bias[i]
  
          # update the conv weight and bias
          _conv_weight = _conv_weight * _scale
          _conv_bias = _scale * (_conv_bias - _mean) + _beta
          new_conv_weight.append(_conv_weight)
          new_conv_bias.append(_conv_bias)
  
      new_conv_weight = np.array(new_conv_weight).astype(
          conv_weight_type
      )
      new_conv_bias = np.array(new_conv_bias).astype(conv_weight_type)
  
      if is_deconv:
          new_conv_weight = np.reshape(
              new_conv_weight,
              [Cout // groups, Cin] + list(new_conv_weight.shape[2:]),
          )
          new_conv_weight = np.transpose(
              new_conv_weight, deconv_weight_transpose_axes
          )
  
      # make sure the updated weight and bias have the same shape as the original ones
      if new_conv_weight.shape != origin_weight_shape:
          raise AssertionError(
              "conv weight should have the same shape before and after the fuse_"
              "conv_batchnorm pass. "
          )
      if new_conv_bias.shape != origin_bias_shape:
          raise AssertionError(
              "conv bias should have the same shape before and after the fuse_"
              "conv_batchnorm pass. "
          )
  
      # create a new conv op with the new bias value, copying rest of the attributes
      out_name = bn_op.outputs[0].name
      conv_kargs = {
          "weight": new_conv_weight,
          "bias": new_conv_bias,
          "name": out_name,
          "before_op": conv_op,
      }
  
      for k, v in conv_op.inputs.items():
          if k in ["weight", "bias"]:
              continue
          conv_kargs[k] = v
  
      if is_deconv:
          x = mb.conv_transpose(**conv_kargs)
      else:
          x = mb.conv(**conv_kargs)
  
      if bn_op.enclosing_block.try_replace_uses_of_var_after_op(
          anchor_op=bn_op,
          old_var=bn_op.outputs[0],
          new_var=x,
      ):
          bn_op.enclosing_block.remove_ops([conv_op, bn_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 2 for calling the fuse_conv_batchnorm obj's underlying member method code with example usage: obj._try_to_transform(conv_op, bn_op) and returns: False, False, False, True