class_docstr: A compile-time operation that returns a constant output value upon decompressing
  a look-up table (LUT) to a dense tensor.
  
  This operation is used to store constant weights in a LUT format (also known as
  `palettized` weights). A LUT is a mapping from index to values.
  Weights are quantized and stored as indices (or keys) into the LUT.
  Before computation, these keys are mapped to corresponding values in the LUT.
  
  Parameters
  ----------
  indices: const tensor<uint8, [M]> (Required)
  
  lut: const tensor<T, [NUM_PALETTES]> (Required)
  
  shape: const tensor<uint32, [K]> (Required)
  
  Notes
  -----
  * Any data is packed and read in a row-major order.
  * ``NUM_PALETTES`` can be one of ``{2, 4, 16, 64 or 256}``.
  * ``n_bits = log2(NUM_PALETTES)`` can thus be one of ``{1, 2, 4, 6, 8}``.
  * Indices are packed in bytes of size ``M``, where ``M = ceil(n_bits * product(shape) / 8)``.
  
  The bit fields are packed one byte at a time, starting with the least significant bit (LSB) and
  moving upward to the most significant bit (MSB). It follows, naturally, that if an index is split
  across two bytes, the LSBs of that index is filled over the MSBs of current byte, and the remaining
  bits of the same index are filled in the LSBs of the next byte.
  
  For example:
  
  .. sourcecode:: python
  
      if n_bits = 2, shape = (5,) => M = 2 bytes
  
                  MSB             LSB
                   |               |
      indices =  | 01   10   11   00 | xx   xx   xx   11 |      <== packed elements
                 | i3 | i2 | i1 | i0 | -- | -- | -- | i4 |      <== tagged element ids
                 |      byte 0       |       byte 1      |      <== tagged bytes
  
  Returns
  -------
  const tensor<T, [...]>
  
  Attributes
  ----------
  T: uint8, int8, fp16, fp32
class_docstr_tok: ["A","compile-time","operation","that","returns","a","constant","output","value","upon","decompressing","a","look-up","table","(","LUT",")","to","a","dense","tensor",".","This","operation","is","used","to","store","constant","weights","in","a","LUT","format","(","also","known","as","`","palettized","`","weights",")",".","A","LUT","is","a","mapping","from","index","to","values",".","Weights","are","quantized","and","stored","as","indices","(","or","keys",")","into","the","LUT",".","Before","computation",",","these","keys","are","mapped","to","corresponding","values","in","the","LUT",".","Parameters","--","--","--","--","--","indices",":","const","tensor","<","uint8",",","[","M","]",">","(","Required",")","lut",":","const","tensor","<","T",",","[","NUM_PALETTES","]",">","(","Required",")","shape",":","const","tensor","<","uint32",",","[","K","]",">","(","Required",")","Notes","--","--","-","*","Any","data","is","packed","and","read","in","a","row-major","order",".","*","``","NUM_PALETTES","``","can","be","one","of","``","{","2",",","4",",","16",",","64","or","256","}","``",".","*","``","n_bits","=","log2","(","NUM_PALETTES",")","``","can","thus","be","one","of","``","{","1",",","2",",","4",",","6",",","8","}","``",".","*","Indices","are","packed","in","bytes","of","size","``","M","``",",","where","``","M","=","ceil","(","n_bits","*","product","(","shape",")","\/","8",")","``",".","The","bit","fields","are","packed","one","byte","at","a","time",",","starting","with","the","least","significant","bit","(","LSB",")","and","moving","upward","to","the","most","significant","bit","(","MSB",")",".","It","follows",",","naturally",",","that","if","an","index","is","split","across","two","bytes",",","the","LSBs","of","that","index","is","filled","over","the","MSBs","of","current","byte",",","and","the","remaining","bits","of","the","same","index","are","filled","in","the","LSBs","of","the","next","byte",".","For","example",":","..","sourcecode",":",":","python","if","n_bits","=","2",",","shape","=","(","5",",",")","=",">","M","=","2","bytes","MSB","LSB","|","|","indices","=","|","01","10","11","00","|","xx","xx","xx","11","|","<","==","packed","elements","|","i3","|","i2","|","i1","|","i0","|","--","|","--","|","--","|","i4","|","<","==","tagged","element","ids","|","byte","0","|","byte","1","|","<","==","tagged","bytes","Returns","--","--","--","-","const","tensor","<","T",",","[","...","]",">","Attributes","--","--","--","--","--","T",":","uint8",",","int8",",","fp16",",","fp32"]
code: def type_inference(self):
      def assert_is_vector(param, name):
          if param.rank != 1:
              raise ValueError(
                  "Parameter {} needs to have rank == 1".format(name)
              )
  
      assert_is_vector(self.indices, "indices")
      assert_is_vector(self.lut, "lut")
  
      if self.lut.shape[0] not in (2, 4, 16, 64, 256):
          raise ValueError(
              "Parameter lut should be a vector of size from one of {2, 4, 16, 64, 256}"
          )
  
      nbits = int(np.log2(self.lut.shape[0]))
      output_size = np.prod(self.shape.val)
      if self.indices.shape[0] != np.ceil(nbits * (output_size / 8.0)):
          raise AssertionError(
              "Constraint violated, M = ceil(n_bits * product(shape) / 8) where M = indices.size"
          )
  
      dtype = self.lut.dtype
      shape = self.shape.val
      return types.tensor(dtype, shape)
  
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 Operation
  from coremltools.converters.mil.mil.ops.defs._op_reqs import register_op
  from coremltools.converters.mil.mil.ops.defs._utils import restore_elements_from_packed_bits
  from coremltools.converters.mil.mil.ops.defs.iOS16 import _IOS16_TARGET
label_desc: Use image node_id 1 for calling the constexpr_lut_to_dense obj's underlying member method code with example usage: obj.type_inference() and returns: types