code: def numerical(model=None, hls_model=None, X=None, plot="boxplot"):
      """Perform numerical profiling of a model.
  
      Args:
          model (optional): Keras of PyTorch model. Defaults to None.
          hls_model (ModelGraph, optional): The ModelGraph to profile. Defaults to None.
          X (ndarray, optional): Test data on which to evaluate the model to profile activations.
              Must be formatted suitably for the ``model.predict(X)``. Defaults to None.
          plot (str, optional): The type of plot to produce. Options are: 'boxplot' (default), 'violinplot', 'histogram',
              'FacetGrid'. Defaults to 'boxplot'.
  
      Returns:
          tuple: The quadruple of produced figures. First weights and biases
              for the pre- and post-optimization models respectively,
              then activations for the pre- and post-optimization models
              respectively. (Optimizations are applied to an ModelGraph by hls4ml,
              a post-optimization ModelGraph is a final model).
      """
      wp, wph, ap, aph = None, None, None, None
  
      hls_model_present = hls_model is not None and isinstance(
          hls_model, ModelGraph
      )
      model_present = model is not None
  
      if hls_model_present:
          before = " (before optimization)"
          after = " (final / after optimization)"
          (
              hls_model_unoptimized,
              tmp_output_dir,
          ) = get_unoptimized_hlsmodel(hls_model)
      else:
          before = ""
          after = ""
          hls_model_unoptimized, tmp_output_dir = None, None
  
      print("Profiling weights" + before)
      data = None
  
      if hls_model_present:
          data = weights_hlsmodel(
              hls_model_unoptimized, fmt="summary", plot=plot
          )
      elif model_present:
          if __tf_profiling_enabled__ and isinstance(
              model, keras.Model
          ):
              data = weights_keras(model, fmt="summary", plot=plot)
          elif __torch_profiling_enabled__ and isinstance(
              model, torch.nn.Sequential
          ):
              data = weights_torch(model, fmt="summary", plot=plot)
  
      if data is None:
          print(
              "Only keras, PyTorch (Sequential) and ModelGraph models "
              + "can currently be profiled"
          )
  
          if hls_model_present and os.path.exists(tmp_output_dir):
              shutil.rmtree(tmp_output_dir)
  
          return wp, wph, ap, aph
  
      wp = plots[plot](data, fmt="summary")  # weight plot
  
      if hls_model_present and plot in types_plots:
          t_data = types_hlsmodel(hls_model_unoptimized)
          types_plots[plot](t_data, fmt="summary")
  
      plt.title("Distribution of (non-zero) weights" + before)
      plt.tight_layout()
  
      if hls_model_present:
          print("Profiling weights" + after)
  
          data = weights_hlsmodel(hls_model, fmt="summary", plot=plot)
          wph = plots[plot](data, fmt="summary")  # weight plot
  
          if plot in types_plots:
              t_data = types_hlsmodel(hls_model)
              types_plots[plot](t_data, fmt="summary")
  
          plt.title("Distribution of (non-zero) weights" + after)
          plt.tight_layout()
  
      if X is not None:
          print("Profiling activations" + before)
          data = None
          if __tf_profiling_enabled__ and isinstance(
              model, keras.Model
          ):
              data = activations_keras(
                  model, X, fmt="summary", plot=plot
              )
          elif __torch_profiling_enabled__ and isinstance(
              model, torch.nn.Sequential
          ):
              data = activations_torch(
                  model, X, fmt="summary", plot=plot
              )
  
          if data is not None:
              ap = plots[plot](data, fmt="summary")  # activation plot
              if hls_model_present and plot in types_plots:
                  t_data = activation_types_hlsmodel(
                      hls_model_unoptimized
                  )
                  types_plots[plot](t_data, fmt="summary")
              plt.title(
                  "Distribution of (non-zero) activations" + before
              )
              plt.tight_layout()
  
          if hls_model_present:
              print("Profiling activations" + after)
              data = activations_hlsmodel(
                  hls_model, X, fmt="summary", plot=plot
              )
              aph = plots[plot](data, fmt="summary")
  
              t_data = activation_types_hlsmodel(hls_model)
              types_plots[plot](t_data, fmt="summary")
  
              plt.title(
                  "Distribution of (non-zero) activations (final / after optimization)"
              )
              plt.tight_layout()
  
      if hls_model_present and os.path.exists(tmp_output_dir):
          shutil.rmtree(tmp_output_dir)
  
      return wp, wph, ap, aph
  
imports: import json
  import os
  import shutil
  import uuid
  from collections import defaultdict
  import matplotlib.pyplot
  import numpy
  import pandas
  import seaborn
  from hls4ml.model.graph import ModelGraph
  from hls4ml.model.layers import GRU, LSTM, SeparableConv1D, SeparableConv2D
label_desc: Use image node_id 19 for calling a global function with example usage: numerical(model, hls_model, X, plot) and returns: wp,  wph,  ap,  aph, wp,  wph,  ap,  aph