_buildDAG() {
    const modelClass = this.modelConfig.class_name

    let modelConfig = []
    if (modelClass === 'Sequential') {
      modelConfig = this.modelConfig.config
    } else if (modelClass === 'Model') {
      modelConfig = this.modelConfig.config.layers
    }

    if (!(Array.isArray(modelConfig) && modelConfig.length)) {
      throw new Error('[Model] Model configuration does not contain any layers.')
    }

    modelConfig.forEach((layerDef, index) => {
      const layerClass = layerDef.class_name
      const layerConfig = layerDef.config

      if (modelClass === 'Model' && layerClass === 'Sequential') {
        // when layer is a Sequential branch in a Model
        layerConfig.forEach((branchLayerDef, branchIndex) => {
          const branchLayerClass = branchLayerDef.class_name
          const branchLayerConfig = branchLayerDef.config

          const branchInboundLayerNames =
            branchIndex === 0
              ? layerDef.inbound_nodes[0].map(node => node[0])
              : [layerConfig[branchIndex - 1].config.name]

          this._createLayer(branchLayerClass, branchLayerConfig, branchInboundLayerNames)
        })
      } else if (!(layerClass in layers)) {
        throw new Error(`[Model] Layer ${layerClass} specified in model configuration is not implemented!`)
      } else {
        // create InputLayer node for Sequential class (which is not explicitly defined in config)
        // create input tensor for InputLayer specified in Model class (layer itself created later)
        if (modelClass === 'Sequential' && index === 0) {
          const inputName = 'input'
          const inputShape = layerConfig.batch_input_shape.slice(1)
          const layer = new layers.InputLayer({ name: inputName, shape: inputShape, gpu: this.gpu })
          this.modelLayersMap.set(inputName, layer)
          this.inputTensorsMap.set(inputName, new Tensor([], inputShape))
          this.inputLayerNames.push(inputName)
        } else if (modelClass === 'Model' && layerClass === 'InputLayer') {
          const inputShape = layerConfig.batch_input_shape.slice(1)
          this.inputTensorsMap.set(layerConfig.name, new Tensor([], inputShape))
          this.inputLayerNames.push(layerConfig.name)
        }

        let inboundLayerNames = []
        if (modelClass === 'Sequential') {
          if (index === 0) {
            inboundLayerNames = ['input']
          } else {
            inboundLayerNames = [modelConfig[index - 1].config.name]
          }
        } else if (modelClass === 'Model') {
          const inboundNodes = layerDef.inbound_nodes
          if (inboundNodes && inboundNodes.length) {
            inboundLayerNames = inboundNodes[0].map(node => node[0])
          }
        }
        this._createLayer(layerClass, layerConfig, inboundLayerNames)
      }
    })

    this.modelLayersMap.forEach(layer => {
      this.modelLayersInfo.push(_.pick(layer, ['name', 'description', 'layerClass', 'inbound', 'outbound']))
      if (layer.outbound.length === 0) {
        this.outputLayerNames.push(layer.name)
      }
    })

    this.inputLayerNames.sort()
    this.outputLayerNames.sort()
  }
Builds directed acyclic graph of model layers

Every layer in the model defines inbound and outbound nodes. For Keras models of class Sequential, we still convert
the list into DAG format for straightforward interoperability with graph models (however, we must first create an
Input layer as the initial layer. For class Model, the DAG is constructed from the configuration inbound and
outbound nodes. Note that Models can have layers be entire Sequential branches.