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 MatMul node, consume nodes to form a dense Ax + b node.
docstr_tok: ["Starting","from","a","MatMul","node",",","consume","nodes","to","form","a","dense","Ax","+","b","node","."]
code: def _consume_dense_nodes(self, node, next_nodes):
      """Starting from a MatMul node, consume nodes to form a dense Ax + b node."""
      if node.op_type != "MatMul":
          raise ValueError(
              f"{node.name} is a {node.op_type} node, only MatMul nodes can be used as starting points for consumption."
          )
      if len(node.input) != 2:
          raise ValueError(
              f"{node.name} input has {len(node.input)} dimensions, only nodes with 2 input dimensions can be used as starting points for consumption."
          )
  
      [in_0, in_1] = list(node.input)
      input_layer, transformer = self._node_input_and_transformer(in_0)
      node_weights = self._initializers[in_1]
  
      if len(next_nodes) != 1:
          raise ValueError(
              f"Next nodes must have length 1, {next_nodes} has length {len(next_nodes)}"
          )
  
      # expect 'Add' node ahead
      type_, node, maybe_next_nodes = self._nodes[next_nodes[0]]
      if type_ != "node":
          raise TypeError(
              f"Expected a node next, got a {type_} instead."
          )
      if node.op_type != "Add":
          raise ValueError(
              f"The first node to be consumed, {node.name}, is a {node.op_type} node. Only Add nodes are supported."
          )
  
      # extract biases
      next_nodes = maybe_next_nodes
      [in_0, in_1] = list(node.input)
  
      if in_0 in self._initializers:
          node_biases = self._initializers[in_0]
      elif in_1 in self._initializers:
          node_biases = self._initializers[in_1]
      else:
          raise ValueError(
              f"Node inputs were not found in graph initializers."
          )
  
      if len(node_weights.shape) != 2:
          raise ValueError(
              f"Node weights must be a 2-dimensional matrix."
          )
      if node_weights.shape[1] != node_biases.shape[0]:
          raise ValueError(
              f"Node weights has {node_weights.shape[1]} columns; node biases has {node_biases.shape[0]} rows. These must be equal."
          )
      if len(node.output) != 1:
          raise ValueError(
              f"Node output is {node.output} but should be a single value."
          )
  
      input_output_size = _get_input_output_size(
          input_layer, transformer
      )
  
      output_size = input_output_size[:-1] + [node_weights.shape[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
  
      dense_layer = DenseLayer(
          input_output_size,
          output_size,
          node_weights,
          node_biases,
          activation=activation,
          input_index_mapper=None,
      )
      self._node_map[node.name] = dense_layer
      self._node_map[node.output[0]] = dense_layer
  
      return next_nodes, dense_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 5 for calling the NetworkParser obj's underlying member method code with example usage: obj._consume_dense_nodes(node, next_nodes) and returns: next_nodes,  dense_layer,  list