class_docstr: Performs affine/linear quantization on an input tensor.
  
  The original data comes from the first "input".
  The other parameters -- ``scale``, ``zero_point``, and ``axis`` -- describe how
  quantization should occur::
  
      quantized_data = clip(round(input / scale) + zero_point)
  
  Parameters
  ----------
  input: tensor<SrcT, [1..]> (Required)
  
  zero_point: const tensor<DstT, [0..1]> (Optional)
      * The ``zero_point`` can be either a scalar or a vector. If not provided, it is
        assumed to be ``0``.
      * The ``zero_point`` follows similar broadcasting rules and size constraints as ``scale``.
  
  scale: const tensor<SrcT, [0..1]> (Required)
      * The ``scale`` can be either a scalar or a vector.
      * If ``scale`` is a vector, for implementation, it is broadcasted to the following shape:
          - The rank of ``scale`` becomes the same as the rank of the input.
          - Constraint: ``size(scale-vector) == input.shape[axis]``.
          - For ``i == axis``, ``scale.shape[i] == input.shape[i]``.
          - For ``i != axis``, ``scale.shape == 1``.
          - For example:
              - Assume ``input.shape = (2, 3, 4, 5)`` and ``axis = 1``.
              - If ``scale`` is a vector, then ``scale.size`` needs to be equal to
                ``input.shape[axis]``; that is, equal to ``3``.
              - This is broadcasted to ``(1, 3, 1, 1)``.
  
  axis: const tensor<int32, []> (Optional)
  
  output_dtype: const tensor<string, []> (Required)
      * This parameter can take ``"uint8"``, ``"int8"`` as values.
      * The ``output_dtype`` value must match the ``zero_point`` dtype.
  
  Returns
  -------
  tensor<DstT, [1..]>
  
  Attributes
  ----------
  SrcT: fp16, fp32
  DstT: uint8, int8
code: def value_inference(self):
      original_data = self.input.val
      if self.zero_point is not None:
          zero_point = self.zero_point.val
      else:
          zero_point = (
              np.int8(0)
              if self.output_dtype.val == "int8"
              else np.uint8(0)
          )
      scale = self.scale.val
      axis = None
      if self.axis is not None:
          axis = self.axis.val
      dtype_info = np.iinfo(zero_point.dtype)
  
      sc = _rank_promoted_to_same_as_data(original_data, axis, scale)
      zp = _rank_promoted_to_same_as_data(
          original_data, axis, zero_point
      )
      val = np.clip(
          np.around(original_data / sc) + zp.astype(np.float32),
          dtype_info.min,
          dtype_info.max,
      )
      return val.astype(zero_point.dtype)
  
file_path: coremltools/coremltools/converters/mil/mil/ops/defs/iOS17/quantization_ops.py
imports: import numpy
  from coremltools.converters.mil.mil import types
  from coremltools.converters.mil.mil.input_type import InputSpec, TensorInputType
  from coremltools.converters.mil.mil.operation import VALUE, Operation, precondition
  from coremltools.converters.mil.mil.ops.defs._op_reqs import register_op
  from coremltools.converters.mil.mil.ops.defs.iOS17 import _IOS17_TARGET
label_desc: Use image node_id 2 for calling the quantize obj's underlying member method code with example usage: obj.value_inference() and returns: val