code: def full_space_dense_layer(net_block, net, layer_block, layer):
      r"""
      Add full-space formulation of the dense layer to the block:
  
      .. math::
  
          \begin{align*}
              y_j = \sum\limits_{i=0}^{F_{in}-1}w_{ij}x_i+b_j, && \forall 0\le j<F_{out}
          \end{align*}
  
      """
      input_layer, input_layer_block = _input_layer_and_block(
          net_block, net, layer
      )
  
      @layer_block.Constraint(layer.output_indexes)
      def dense_layer(b, *output_index):
          # dense layers multiply only the last dimension of
          # their inputs
          expr = 0.0
          for (
              local_index,
              input_index,
          ) in layer.input_indexes_with_input_layer_indexes:
              w = layer.weights[local_index[-1], output_index[-1]]
              expr += input_layer_block.z[input_index] * w
          # move this at the end to avoid numpy/pyomo var bug
          expr += layer.biases[output_index[-1]]
  
          lb, ub = compute_bounds_on_expr(expr)
          layer_block.zhat[output_index].setlb(lb)
          layer_block.zhat[output_index].setub(ub)
  
          return layer_block.zhat[output_index] == expr
  
file_path: OMLT/src/omlt/neuralnet/layers/full_space.py
imports: import numpy
  import pyomo.environ
  from pyomo.contrib.fbbt.fbbt import compute_bounds_on_expr
  from omlt.neuralnet.activations import NON_INCREASING_ACTIVATIONS
  from omlt.neuralnet.layer import ConvLayer2D, IndexMapper, PoolingLayer2D
label_desc: Use image node_id 1 for calling a global function with example usage: full_space_dense_layer(net_block, net, layer_block, layer) and returns: unknown