class_docstr: References
  ----------
  * https://github.com/onnx/onnx/blob/master/docs/Operators.md
class_docstr_tok: ["References","--","--","--","--","--","*","https",":","\/\/github.com\/onnx\/onnx\/blob\/master\/docs\/Operators.md"]
docstr: Starting from a MaxPool node, consume nodes to form a pooling node with
  (optional) activation function.
docstr_tok: ["Starting","from","a","MaxPool","node",",","consume","nodes","to","form","a","pooling","node","with","(","optional",")","activation","function","."]
code: def _consume_pool_nodes(self, node, next_nodes):
      """
      Starting from a MaxPool node, consume nodes to form a pooling node with
      (optional) activation function.
      """
      if node.op_type not in _POOLING_OP_TYPES:
          raise ValueError(
              f"{node.name} is a {node.op_type} node, only MaxPool nodes can be used as starting points for consumption."
          )
      pool_func_name = "max"
  
      # ONNX network should not contain indices output from MaxPool - not supported by OMLT
      if len(node.output) != 1:
          raise ValueError(
              f"The ONNX contains indices output from MaxPool. This is not supported by OMLT."
          )
      if len(node.input) != 1:
          raise ValueError(
              f"{node.name} input has {len(node.input)} dimensions, only nodes with 1 input dimension can be used as starting points for consumption."
          )
  
      input_layer, transformer = self._node_input_and_transformer(
          node.input[0]
      )
      input_output_size = _get_input_output_size(
          input_layer, transformer
      )
  
      # currently only support 2D image with channels.
      if len(input_output_size) == 4:
          # this means there is an extra dimension for number of batches
          # batches not supported, so only accept if they're not there or there is only 1 batch
          if input_output_size[0] != 1:
              raise ValueError(
                  f"{node.name} has {input_output_size[0]} batches, only a single batch is supported."
              )
          input_output_size = input_output_size[1:]
  
      in_channels = input_output_size[0]
  
      attr = _collect_attributes(node)
      kernel_depth = attr["kernel_shape"][0]
      kernel_shape = attr["kernel_shape"][1:]
      strides = (
          attr["strides"]
          if "strides" in attr
          else [1] * len(kernel_shape)
      )
  
      # check only kernel shape, stride, storage order are set
      # everything else is not supported
      if "dilations" in attr and attr["dilations"] != [1, 1]:
          raise ValueError(
              f"{node.name} has non-identity dilations ({attr['dilations']}). This is not supported."
          )
      if "pads" in attr and np.any(attr["pads"]):
          raise ValueError(
              f"{node.name} has non-zero pads ({attr['pads']}). This is not supported."
          )
      if ("auto_pad" in attr) and (attr["auto_pad"] != "NOTSET"):
          raise ValueError(
              f"{node.name} has autopad set ({attr['auto_pad']}). This is not supported."
          )
      if len(kernel_shape) != len(strides):
          raise ValueError(
              f"Kernel shape {kernel_shape} has {len(kernel_shape)} dimensions. Strides attribute has {len(strides)} dimensions. These must be equal."
          )
      if len(input_output_size) != len(kernel_shape) + 1:
          raise ValueError(
              f"Input/output size ({input_output_size}) must have one more dimension than kernel shape ({kernel_shape})."
          )
  
      output_shape_wrapper = math.floor
      if "ceil_mode" in attr and attr["ceil_mode"] == 1:
          output_shape_wrapper = math.ceil
  
      output_size = [in_channels]
      for i in range(1, len(input_output_size)):
          output_size.append(
              output_shape_wrapper(
                  (input_output_size[i] - kernel_shape[i - 1])
                  / strides[i - 1]
                  + 1
              )
          )
  
      activation = "linear"
      if len(next_nodes) == 1:
          # check if Relu
          type_, maybe_node, maybe_next_nodes = self._nodes[
              next_nodes[0]
          ]
          if maybe_node.op_type in _ACTIVATION_OP_TYPES:
              node = maybe_node
              activation = maybe_node.op_type.lower()
              next_nodes = maybe_next_nodes
  
      pooling_layer = PoolingLayer2D(
          input_output_size,
          output_size,
          strides,
          pool_func_name,
          tuple(kernel_shape),
          kernel_depth,
          activation=activation,
          input_index_mapper=transformer,
      )
      self._node_map[node.name] = pooling_layer
      self._node_map[node.output[0]] = pooling_layer
  
      return next_nodes, pooling_layer, [input_layer]
  
file_path: OMLT/src/omlt/io/onnx_parser.py
imports: import math
  import numpy
  from onnx import numpy_helper
  from omlt.neuralnet.layer import ConvLayer2D, DenseLayer, IndexMapper, InputLayer, PoolingLayer2D
  from omlt.neuralnet.network_definition import NetworkDefinition
label_desc: Use image node_id 9 for calling the NetworkParser obj's underlying member method code with example usage: obj._consume_pool_nodes(node, next_nodes) and returns: next_nodes,  pooling_layer,  list