class_docstr: Perform convolution over input, currently supporting only 1D and 2D
  convolution.
  
  Parameters
  ----------
  x: tensor<[n, C_in, *d_in], T> (Required)
  
      * ``d_in`` are (possibly runtime-determined) spatial dimensions (e.g., ``d_in = [224, 224]`` for 2D convolution).  ``1 <= len(d_in) <= 2`` (i.e., only 1D and 2D convolution) .
      * ``C_in`` is the number of input channels or depth dimensions.
      * ``n``  is the batch dimension
  
  weight: tensor<[C_out, C_in/groups, *K], T> (Required)
  
      * Filter weights. ``C_in`` is the number of input channels. ``C_in`` must be divisible by ``groups``
      * ``K`` are kernel sizes (e.g. ``K = [KH, KW]`` for 2D conv).
      * When ``dilations`` is not all 1, ``weight`` has to be const at compile time
  
  strides: const tensor<[S], i32> (Optional. Default to one vector of length equals to the number of spatial dimensions)
      * Strides along each of the spatial dimensions
      * ``S == len(d_in)``
  
  pad_type: const str (Required)
  
      * Must be one of the followings
  
          * ``valid``: No padding. This is equivalent to custom pad with ``pad[2*i] == pad[2*i+1] == 0, for i=0,...,len(d_in)-1``
  
          * ``same``: input is padded such that out spatial shapes are ``d_out[i] = ceil(d_in[i] / strides[i])``. Specifically, for ``i = 0,..,,len(d_in)-1``, the equivalent paddings are
  
              * When dilated kernel is even (i.e.  ``(K[i]-1)*dilations[i]+1)``
                  *  ``pad[2*i] = ceil[((K[i]-1)*dilations[i]+1)/2]``
                  *  ``pad[2*i+1] = floor[((K[i]-1)*dilations[i]+1)/2]``
              * Otherwise,  ``pad[2*i] = pad[2*i+1] = (K[i]-1) * dilations[i] / 2``
          * custom: Specify custom padding in the parameter ``pad``
  
  pad: const tensor<[P], i32> (Optional. Default to all zeros)
      * ``len(P) = 2 * len(d_in)``
      * ``pad`` should be specified if and only if ``pad_type == custom``.  Errors otherwise.
      *  ``pad`` represents the number of elements to pad before and after each dimension. Specifically, ``pad[0], pad[1]`` are the pad size before / after spatial dimension 0, ``pad[2], pad[3]`` are the pad size before / after spatial dimension 1, etc.
  
  dilations: const tensor<[S], i32> (Optional. Default to all 1s)
      * Dilation value along each spatial dimension in ``d_in``. See `visualization <https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md>`_
      * ``S == len(d_in)``
  
  groups: const tensor<[], i32> (Optional. Default to 1)
      * Input and output channels are split by ``groups``.
  
      * ``C_in`` must be divisible by ``groups``. Maximum value for group is ``C_in``, in which case it is a depthwise convolution.
  
      * For examples (assuming ``C_in = 16, C_out = 32``):
  
          * ``groups == 1``, ``weight`` has shape ``[32, 16, KH, KW]``: all input channels are convolved with ``weight`` kernel to produce all output channels
  
          * ``groups == 2``, ``weight`` has shape ``[32, 8, KH, KW]``: Input channel 0~7 are convolved with half of ``weight`` kernel to produce output channel 0~15. Similarly, input channel 8~15 are convolved with the other half of ``weight`` to product output channel 16~31.
  
          * ``groups == C_in``, ``weight`` has shape ``[32, 1, KH, KW]``: each input channel is convolved with its own set of filters and each produce ``C_out / C_in = 2`` channels. This is equivalent to depthwise convolution.
  
  bias: const tensor<[C_out],T> (Optional. Default to all 0)
      * Bias along output channels
  
  
  Returns
  -------
  tensor<[n, C_out, *d_out], T>
      * Output activation has the same rank and spatial dimension as the input (i.e., ``len(d_out) == len(d_in)``)
      * For ``i=0,..,len(d_in)-1, d_out[i] = floor [(D_in[i] + pad[2*i] + pad[2*i+1] - (K[i]-1)*dilations[i] - 1) / strides[i] ] + 1``
  
  Attributes
  ----------
  T: fp32
  
  See Also
  --------
  conv_transpose
code: def type_inference(self):
      inshape = self.x.shape
      f_shape = self.weight.shape
      kernel_shape = f_shape[2:]
      num_dims = len(inshape) - 2
      C_out = f_shape[0]
      C_in = self.x.shape[1]
      groups = self.groups.val
  
      if self.bias is not None and self.bias.val.shape[0] != C_out:
          msg = "# of bias values {} not equal to # output channels {}"
      if C_in % groups != 0:
          msg = "# of input channels {} not divisible by groups {}"
          raise ValueError(msg.format(C_in, groups))
      if C_in // groups != self.weight.shape[1]:
          msg = "C_in / groups = {}/{} != weight[1] ({})"
          raise ValueError(
              msg.format(C_in, groups, self.weight.shape[1])
          )
  
      strides = (
          [1] * num_dims if self.strides is None else self.strides.val
      )
      dilations = (
          [1] * num_dims
          if self.dilations is None
          else self.dilations.val
      )
      custom_pad = None if self.pad is None else self.pad.val
      N = inshape[0]
      C_out = f_shape[0]
      # spatial dimensions
      d_out_shape = spatial_dimensions_out_shape(
          pad_type=self.pad_type.val,
          input_shape=inshape[2:],
          kernel_shape=kernel_shape,
          strides=strides,
          dilations=dilations,
          custom_pad=custom_pad,
      )
      retshape = [N, C_out] + d_out_shape
      return types.tensor(self.x.dtype, tuple(retshape))
  
file_path: turicreate/src/external/coremltools_wrap/coremltools/coremltools/converters/mil/mil/ops/defs/conv.py
imports: from coremltools.converters.mil.mil.ops.defs._utils import spatial_dimensions_out_shape
  from ._op_reqs import *
label_desc: Use image node_id 2 for calling the conv obj's underlying member method code with example usage: obj.type_inference() and returns: types