class_docstr: Directed Acyclic Graph models for molecular property prediction.
  
  This model is based on the following paper:
  
  Lusci, Alessandro, Gianluca Pollastri, and Pierre Baldi. "Deep architectures and deep learning in chemoinformatics: the prediction of aqueous solubility for drug-like molecules." Journal of chemical information and modeling 53.7 (2013): 1563-1575.
  
  The basic idea for this paper is that a molecule is usually
  viewed as an undirected graph. However, you can convert it to
  a series of directed graphs. The idea is that for each atom,
  you make a DAG using that atom as the vertex of the DAG and
  edges pointing "inwards" to it. This transformation is
  implemented in
  `dc.trans.transformers.DAGTransformer.UG_to_DAG`.
  
  This model accepts ConvMols as input, just as GraphConvModel
  does, but these ConvMol objects must be transformed by
  dc.trans.DAGTransformer.
  
  As a note, performance of this model can be a little
  sensitive to initialization. It might be worth training a few
  different instantiations to get a stable set of parameters.
class_docstr_tok: ["Directed","Acyclic","Graph","models","for","molecular","property","prediction",".","This","model","is","based","on","the","following","paper",":","Lusci",",","Alessandro",",","Gianluca","Pollastri",",","and","Pierre","Baldi",".","``","Deep","architectures","and","deep","learning","in","chemoinformatics",":","the","prediction","of","aqueous","solubility","for","drug-like","molecules",".","''","Journal","of","chemical","information","and","modeling","53.7","(","2013",")",":","1563-1575",".","The","basic","idea","for","this","paper","is","that","a","molecule","is","usually","viewed","as","an","undirected","graph",".","However",",","you","can","convert","it","to","a","series","of","directed","graphs",".","The","idea","is","that","for","each","atom",",","you","make","a","DAG","using","that","atom","as","the","vertex","of","the","DAG","and","edges","pointing","``","inwards","''","to","it",".","This","transformation","is","implemented","in","`","dc.trans.transformers.DAGTransformer.UG_to_DAG","`",".","This","model","accepts","ConvMols","as","input",",","just","as","GraphConvModel","does",",","but","these","ConvMol","objects","must","be","transformed","by","dc.trans.DAGTransformer",".","As","a","note",",","performance","of","this","model","can","be","a","little","sensitive","to","initialization",".","It","might","be","worth","training","a","few","different","instantiations","to","get","a","stable","set","of","parameters","."]
docstr: Parameters
  ----------
  n_tasks: int
      Number of tasks.
  max_atoms: int, optional
      Maximum number of atoms in a molecule, should be defined based on dataset.
  n_atom_feat: int, optional
      Number of features per atom.
  n_graph_feat: int, optional
      Number of features for atom in the graph.
  n_outputs: int, optional
      Number of features for each molecule.
  layer_sizes: list of int, optional
      List of hidden layer size(s) in the propagation step:
      length of this list represents the number of hidden layers,
      and each element is the width of corresponding hidden layer.
  layer_sizes_gather: list of int, optional
      List of hidden layer size(s) in the gather step.
  dropout: None or float, optional
      Dropout probability, applied after each propagation step and gather step.
  mode: str, optional
      Either "classification" or "regression" for type of model.
  n_classes: int
      the number of classes to predict (only used in classification mode)
  uncertainty: bool
      if True, include extra outputs and loss terms to enable the uncertainty
      in outputs to be predicted
docstr_tok: ["Parameters","--","--","--","--","--","n_tasks",":","int","Number","of","tasks",".","max_atoms",":","int",",","optional","Maximum","number","of","atoms","in","a","molecule",",","should","be","defined","based","on","dataset",".","n_atom_feat",":","int",",","optional","Number","of","features","per","atom",".","n_graph_feat",":","int",",","optional","Number","of","features","for","atom","in","the","graph",".","n_outputs",":","int",",","optional","Number","of","features","for","each","molecule",".","layer_sizes",":","list","of","int",",","optional","List","of","hidden","layer","size","(","s",")","in","the","propagation","step",":","length","of","this","list","represents","the","number","of","hidden","layers",",","and","each","element","is","the","width","of","corresponding","hidden","layer",".","layer_sizes_gather",":","list","of","int",",","optional","List","of","hidden","layer","size","(","s",")","in","the","gather","step",".","dropout",":","None","or","float",",","optional","Dropout","probability",",","applied","after","each","propagation","step","and","gather","step",".","mode",":","str",",","optional","Either","``","classification","''","or","``","regression","''","for","type","of","model",".","n_classes",":","int","the","number","of","classes","to","predict","(","only","used","in","classification","mode",")","uncertainty",":","bool","if","True",",","include","extra","outputs","and","loss","terms","to","enable","the","uncertainty","in","outputs","to","be","predicted"]
code: def __init__(
      self,
      n_tasks,
      max_atoms=50,
      n_atom_feat=75,
      n_graph_feat=30,
      n_outputs=30,
      layer_sizes=[100],
      layer_sizes_gather=[100],
      dropout=None,
      mode="classification",
      n_classes=2,
      uncertainty=False,
      batch_size=100,
      **kwargs,
  ):
      """
      Parameters
      ----------
      n_tasks: int
          Number of tasks.
      max_atoms: int, optional
          Maximum number of atoms in a molecule, should be defined based on dataset.
      n_atom_feat: int, optional
          Number of features per atom.
      n_graph_feat: int, optional
          Number of features for atom in the graph.
      n_outputs: int, optional
          Number of features for each molecule.
      layer_sizes: list of int, optional
          List of hidden layer size(s) in the propagation step:
          length of this list represents the number of hidden layers,
          and each element is the width of corresponding hidden layer.
      layer_sizes_gather: list of int, optional
          List of hidden layer size(s) in the gather step.
      dropout: None or float, optional
          Dropout probability, applied after each propagation step and gather step.
      mode: str, optional
          Either "classification" or "regression" for type of model.
      n_classes: int
          the number of classes to predict (only used in classification mode)
      uncertainty: bool
          if True, include extra outputs and loss terms to enable the uncertainty
          in outputs to be predicted
      """
      if mode not in ["classification", "regression"]:
          raise ValueError(
              "mode must be either 'classification' or 'regression'"
          )
      self.n_tasks = n_tasks
      self.max_atoms = max_atoms
      self.n_atom_feat = n_atom_feat
      self.n_graph_feat = n_graph_feat
      self.n_outputs = n_outputs
      self.layer_sizes = layer_sizes
      self.layer_sizes_gather = layer_sizes_gather
      self.dropout = dropout
      self.mode = mode
      self.n_classes = n_classes
      self.uncertainty = uncertainty
      if uncertainty:
          if mode != "regression":
              raise ValueError(
                  "Uncertainty is only supported in regression mode"
              )
          if dropout is None or dropout == 0.0:
              raise ValueError(
                  "Dropout must be included to predict uncertainty"
              )
  
      # Build the model.
  
      atom_features = Input(shape=(self.n_atom_feat,))
      parents = Input(
          shape=(self.max_atoms, self.max_atoms), dtype=tf.int32
      )
      calculation_orders = Input(
          shape=(self.max_atoms,), dtype=tf.int32
      )
      calculation_masks = Input(shape=(self.max_atoms,), dtype=tf.bool)
      membership = Input(shape=tuple(), dtype=tf.int32)
      n_atoms = Input(shape=tuple(), dtype=tf.int32)
      dag_layer1 = layers.DAGLayer(
          n_graph_feat=self.n_graph_feat,
          n_atom_feat=self.n_atom_feat,
          max_atoms=self.max_atoms,
          layer_sizes=self.layer_sizes,
          dropout=self.dropout,
          batch_size=batch_size,
      )(
          [
              atom_features,
              parents,
              calculation_orders,
              calculation_masks,
              n_atoms,
          ]
      )
      dag_gather = layers.DAGGather(
          n_graph_feat=self.n_graph_feat,
          n_outputs=self.n_outputs,
          max_atoms=self.max_atoms,
          layer_sizes=self.layer_sizes_gather,
          dropout=self.dropout,
      )([dag_layer1, membership])
      n_tasks = self.n_tasks
      if self.mode == "classification":
          n_classes = self.n_classes
          logits = Reshape((n_tasks, n_classes))(
              Dense(n_tasks * n_classes)(dag_gather)
          )
          output = Softmax()(logits)
          outputs = [output, logits]
          output_types = ["prediction", "loss"]
          loss = SoftmaxCrossEntropy()
      else:
          output = Dense(n_tasks)(dag_gather)
          if self.uncertainty:
              log_var = Dense(n_tasks)(dag_gather)
              var = Activation(tf.exp)(log_var)
              outputs = [output, var, output, log_var]
              output_types = ["prediction", "variance", "loss", "loss"]
  
              def loss(outputs, labels, weights):
                  (
                      output,
                      labels,
                  ) = dc.models.losses._make_tf_shapes_consistent(
                      outputs[0], labels[0]
                  )
                  output, labels = dc.models.losses._ensure_float(
                      output, labels
                  )
                  losses = (
                      tf.square(output - labels) / tf.exp(outputs[1])
                      + outputs[1]
                  )
                  w = weights[0]
                  if len(w.shape) < len(losses.shape):
                      if tf.is_tensor(w):
                          shape = tuple(w.shape.as_list())
                      else:
                          shape = w.shape
                      shape = tuple(
                          -1 if x is None else x for x in shape
                      )
                      w = tf.reshape(
                          w,
                          shape
                          + (1,) * (len(losses.shape) - len(w.shape)),
                      )
                  return tf.reduce_mean(losses * w) + sum(
                      self.model.losses
                  )
  
          else:
              outputs = [output]
              output_types = ["prediction"]
              loss = L2Loss()
      model = tf.keras.Model(
          inputs=[
              atom_features,
              parents,
              calculation_orders,
              calculation_masks,
              membership,
              n_atoms,  # dropout_switch
          ],
          outputs=outputs,
      )
      super(DAGModel, self).__init__(
          model,
          loss,
          output_types=output_types,
          batch_size=batch_size,
          **kwargs,
      )
  
imports: from collections.abc import Sequence
  import deepchem
  import numpy
  import tensorflow
  from typing import List, Union, Tuple, Iterable, Dict, Optional
  from deepchem.utils.typing import OneOrMany, LossFn, ActivationFn
  from deepchem.data import Dataset, pad_features
  from deepchem.feat.mol_graphs import ConvMol
  from deepchem.metrics import to_one_hot
  from deepchem.models import KerasModel, layers
  from deepchem.models.losses import L2Loss, SoftmaxCrossEntropy, Loss
  from tensorflow.keras.layers import Input, Dense, Reshape, Softmax, Dropout, Activation, BatchNormalization
  import warnings
label_desc: Use image node_id 1 to create a new DAGModel object from inherited base classes: KerasModel with example: obj = DAGModel(n_tasks, max_atoms, n_atom_feat, n_graph_feat, n_outputs, layer_sizes, layer_sizes_gather, dropout, mode, n_classes, uncertainty, batch_size)