Commit 0dfcb436 authored by miaecle's avatar miaecle
Browse files

yapf and docs

parent 0a793fe5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ def pad_batch(batch_size, X_b, y_b, w_b, ids_b):

    # Fill in batch arrays
    start = 0
    # Only the first set of copy will be counted in training loss
    w_out[start:start + num_samples] = w_b[:]
    while start < batch_size:
      num_left = batch_size - start
+25 −11
Original line number Diff line number Diff line
@@ -162,9 +162,9 @@ def atom_features(atom, bool_id_feat=False, explicit_H=False):
            'Hg',
            'Pb',
            'Unknown'
            ]) + one_of_k_encoding(atom.GetDegree(), [
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
            ])
        ]) + one_of_k_encoding(atom.GetDegree(),
                               [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    # In case of explicit hydrogen(QM8, QM9), avoid calling `GetTotalNumHs`
    if explicit_H:
      results = results + \
        one_of_k_encoding_unk(atom.GetImplicitValence(), [0, 1, 2, 3, 4, 5, 6]) + \
@@ -192,11 +192,13 @@ def bond_features(bond):
  return np.array([
      bt == Chem.rdchem.BondType.SINGLE, bt == Chem.rdchem.BondType.DOUBLE,
      bt == Chem.rdchem.BondType.TRIPLE, bt == Chem.rdchem.BondType.AROMATIC,
      bond.GetIsConjugated(), bond.IsInRing()
      bond.GetIsConjugated(),
      bond.IsInRing()
  ])


def pair_features(mol, edge_list, canon_adj_list, bt_len=6, graph_distance=True):
def pair_features(mol, edge_list, canon_adj_list, bt_len=6,
                  graph_distance=True):
  if graph_distance:
    max_distance = 7
  else:
@@ -220,6 +222,7 @@ def pair_features(mol, edge_list, canon_adj_list, bt_len=6, graph_distance=True)
      distance = find_distance(
          a1, num_atoms, canon_adj_list, max_distance=max_distance)
      features[a1, :, bt_len + 1:] = distance
  # Euclidean distance between atoms
  if not graph_distance:
    coords = np.zeros((N, 3))
    for atom in range(N):
@@ -288,23 +291,29 @@ class WeaveFeaturizer(Featurizer):
  name = ['weave_mol']

  def __init__(self, graph_distance=True, explicit_H=None):
    # Set dtype
    # Distance is either graph distance(True) or Euclidean distance(False,
    # only support datasets providing Cartesian coordinates)
    self.graph_distance = graph_distance
    # Set dtype
    self.dtype = object
    # Check if there are explicit hydrogens, default to be False
    self.check_H = False
    if explicit_H is None:
      self.explicit_H = False
      # Set to True if explicit hydrogen is not specified
      self.check_H = True

  def _featurize(self, mol):
    """Encodes mol as a WeaveMol object."""
    # Atom features
    # Check hydrogen in the molecule
    if self.check_H and not self.explicit_H:
      for a in mol.GetAtoms():
        if a.GetSymbol() == 'H':
          self.explicit_H = True
          break
    idx_nodes = [(a.GetIdx(), atom_features(a, explicit_H=self.explicit_H)) for a in mol.GetAtoms()]
    # Atom features
    idx_nodes = [(a.GetIdx(), atom_features(a, explicit_H=self.explicit_H))
                 for a in mol.GetAtoms()]
    idx_nodes.sort()  # Sort by ind to ensure same order as rd_kit
    idx, nodes = list(zip(*idx_nodes))

@@ -314,8 +323,8 @@ class WeaveFeaturizer(Featurizer):
    # Get bond lists
    edge_list = {}
    for b in mol.GetBonds():
      edge_list[tuple(sorted([b.GetBeginAtomIdx(), b.GetEndAtomIdx()
                             ]))] = bond_features(b)
      edge_list[tuple(sorted([b.GetBeginAtomIdx(),
                              b.GetEndAtomIdx()]))] = bond_features(b)

    # Get canonical adjacency list
    canon_adj_list = [[] for mol_id in range(len(nodes))]
@@ -324,6 +333,11 @@ class WeaveFeaturizer(Featurizer):
      canon_adj_list[edge[1]].append(edge[0])

    # Calculate pair features
    pairs = pair_features(mol, edge_list, canon_adj_list, bt_len=6, graph_distance=self.graph_distance)
    pairs = pair_features(
        mol,
        edge_list,
        canon_adj_list,
        bt_len=6,
        graph_distance=self.graph_distance)

    return WeaveMol(nodes, pairs)
+29 −23
Original line number Diff line number Diff line
@@ -800,8 +800,10 @@ class DAGGather(Layer):
      outputs = self.activation(outputs)
    return outputs


class MessagePassing(Layer):
  """ General class for MPNN """
  """ General class for MPNN
  default structures built according to https://arxiv.org/abs/1511.06391 """

  def __init__(self,
               T,
@@ -830,8 +832,9 @@ class MessagePassing(Layer):

  def build(self, pair_features, n_pair_features):
    if self.message_fn == 'enn':
      self.message_function = EdgeNetwork(pair_features,
                                          n_pair_features,
      # Default message function: edge network, update function: GRU
      # more options to be implemented
      self.message_function = EdgeNetwork(pair_features, n_pair_features,
                                          self.n_hidden)
    if self.update_fn == 'gru':
      self.update_function = GatedRecurrentUnit(self.n_hidden)
@@ -870,8 +873,10 @@ class MessagePassing(Layer):
      self.out_tensor = out_tensor
    return out_tensor


class EdgeNetwork(object):
  """ Submodule for Message Passing """

  def __init__(self,
               pair_features,
               n_pair_features=8,
@@ -892,8 +897,10 @@ class EdgeNetwork(object):
    out = tf.segment_sum(out, atom_to_pair[:, 0])
    return out


class GatedRecurrentUnit(object):
  """ Submodule for Message Passing """

  def __init__(self, n_hidden=100, init='glorot_uniform'):
    self.n_hidden = n_hidden
    self.init = initializations.get(init)
@@ -906,9 +913,10 @@ class GatedRecurrentUnit(object):
    self.bz = model_ops.zeros(shape=(n_hidden,))
    self.br = model_ops.zeros(shape=(n_hidden,))
    self.bh = model_ops.zeros(shape=(n_hidden,))
    self.trainable_weights = [self.Wz, self.Wr, self.Wh,
                              self.Uz, self.Ur, self.Uh,
                              self.bz, self.br, self.bh]
    self.trainable_weights = [
        self.Wz, self.Wr, self.Wh, self.Uz, self.Ur, self.Uh, self.bz, self.br,
        self.bh
    ]

  def forward(self, inputs, messages):
    z = tf.nn.sigmoid(tf.matmul(messages, self.Wz) + \
@@ -920,24 +928,19 @@ class GatedRecurrentUnit(object):
         z * inputs
    return h


class SetGather(Layer):
  """ General class for MPNN """
  """ set2set gather layer for graph-based model 
  model using this layer must set pad_batches=True """

  def __init__(self,
               M,
               batch_size,
               n_hidden=100,
               init='orthogonal',
               **kwargs):
  def __init__(self, M, batch_size, n_hidden=100, init='orthogonal', **kwargs):
    """
        Parameters
        ----------
        T: int
          Number of message passing steps
        message_fn: str, optional
          message function in the model
        update_fn: str, optional
          update function in the model
        M: int
          Number of LSTM steps
        batch_size: int
          Number of samples in a batch(all batches must have same size)
        n_hidden: int, optional
          number of hidden units in the passing phase
        """
@@ -957,7 +960,8 @@ class SetGather(Layer):
    self.trainable_weights = [self.U, self.b]

  def create_tensor(self, in_layers=None, set_tensors=True, **kwargs):
    """ Perform T steps of message passing """
    """ Perform M steps of set2set gather,
        detailed descriptions in: https://arxiv.org/abs/1511.06391 """
    if in_layers is None:
      in_layers = self.in_layers
    in_layers = convert_to_layers(in_layers)
@@ -975,9 +979,12 @@ class SetGather(Layer):
      e = tf.reduce_sum(atom_features * q_expanded, 1)
      e_mols = tf.dynamic_partition(e, atom_split, self.batch_size)
      # Add another value(~-Inf) to prevent error in softmax
      e_mols = [tf.concat([e_mol, tf.constant([-1000.])], 0) for e_mol in e_mols]
      e_mols = [
          tf.concat([e_mol, tf.constant([-1000.])], 0) for e_mol in e_mols
      ]
      a = tf.concat([tf.nn.softmax(e_mol)[:-1] for e_mol in e_mols], 0)
      r = tf.segment_sum(tf.reshape(a, [-1, 1]) * atom_features, atom_split)
      # Model using this layer must set pad_batches=True
      q_star = tf.concat([h, r], axis=1)
      h, c = self.LSTMStep(q_star, c)

@@ -988,8 +995,7 @@ class SetGather(Layer):
    return out_tensor

  def LSTMStep(self, h, c, x=None):

    # Taken from Keras code [citation needed]
    # Perform one step of LSTM
    z = tf.nn.xw_plus_b(h, self.U, self.b)
    i = tf.nn.sigmoid(z[:, :self.n_hidden])
    f = tf.nn.sigmoid(z[:, self.n_hidden:2 * self.n_hidden])
+19 −9
Original line number Diff line number Diff line
@@ -680,7 +680,10 @@ class GraphConvTensorGraph(TensorGraph):

    return y_


class MPNNTensorGraph(TensorGraph):
  """ Message Passing Neural Network,
  default structures built according to https://arxiv.org/abs/1511.06391 """

  def __init__(self,
               n_tasks,
@@ -715,25 +718,29 @@ class MPNNTensorGraph(TensorGraph):
    self.build_graph()

  def build_graph(self):
    # Build placeholders
    self.atom_features = Feature(shape=(None, self.n_atom_feat))
    self.pair_features = Feature(shape=(None, self.n_pair_feat))
    self.atom_split = Feature(shape=(None,), dtype=tf.int32)
    self.atom_to_pair = Feature(shape=(None, 2), dtype=tf.int32)

    message_passing = MessagePassing(self.T,
    message_passing = MessagePassing(
        self.T,
        message_fn='enn',
        update_fn='gru',
        n_hidden=self.n_hidden,
                                     in_layers=[self.atom_features,
                                                self.pair_features,
                                                self.atom_to_pair])
        in_layers=[self.atom_features, self.pair_features, self.atom_to_pair])

    atom_embeddings = Dense(self.n_hidden, in_layers=[message_passing])
    mol_embeddings = SetGather(self.M,

    mol_embeddings = SetGather(
        self.M,
        self.batch_size,
        n_hidden=self.n_hidden,
        in_layers=[atom_embeddings, self.atom_split])

    dense1 = Dense(out_channels=2*self.n_hidden,
    dense1 = Dense(
        out_channels=2 * self.n_hidden,
        activation_fn=tf.nn.relu,
        in_layers=[mol_embeddings])
    costs = []
@@ -771,9 +778,7 @@ class MPNNTensorGraph(TensorGraph):
                        epochs=1,
                        predict=False,
                        pad_batches=True):
    """ TensorGraph style implementation
        similar to deepchem.models.tf_new_models.graph_topology.AlternateWeaveTopology.batch_to_feed_dict
        """
    """ Same generator as Weave models """
    for epoch in range(epochs):
      if not predict:
        print('Starting epoch %i' % epoch)
@@ -789,6 +794,7 @@ class MPNNTensorGraph(TensorGraph):
              feed_dict[label] = to_one_hot(y_b[:, index])
            if self.mode == "regression":
              feed_dict[label] = y_b[:, index:index + 1]
        # w_b act as the indicator of unique samples in the batch
        if w_b is not None:
          feed_dict[self.weights] = w_b

@@ -826,10 +832,12 @@ class MPNNTensorGraph(TensorGraph):
        yield feed_dict

  def predict(self, dataset, transformers=[], batch_size=None):
    # MPNN only accept padded input
    generator = self.default_generator(dataset, predict=True, pad_batches=True)
    return self.predict_on_generator(generator, transformers)

  def predict_proba(self, dataset, transformers=[], batch_size=None):
    # MPNN only accept padded input
    generator = self.default_generator(dataset, predict=True, pad_batches=True)
    return self.predict_proba_on_generator(generator, transformers)

@@ -847,6 +855,7 @@ class MPNNTensorGraph(TensorGraph):
        out_tensors = [x.out_tensor for x in self.outputs]
        results = []
        for feed_dict in generator:
          # Extract number of unique samples in the batch from w_b
          n_valid_samples = len(np.nonzero(feed_dict[self.weights][:, 0])[0])
          feed_dict = {
              self.layers[k.name].out_tensor: v
@@ -857,5 +866,6 @@ class MPNNTensorGraph(TensorGraph):
          if len(result.shape) == 3:
            result = np.transpose(result, axes=[1, 0, 2])
          result = undo_transforms(result, transformers)
          # Only fetch the first set of unique samples
          results.append(result[:n_valid_samples])
        return np.concatenate(results, axis=0)
+1 −4
Original line number Diff line number Diff line
@@ -16,10 +16,7 @@ tasks, datasets, transformers = dc.molnet.load_qm8(featurizer='MP')
train_dataset, valid_dataset, test_dataset = datasets

# Fit models
metric = [
    dc.metrics.Metric(dc.metrics.mean_absolute_error, np.mean, mode="regression"),
    dc.metrics.Metric(dc.metrics.pearson_r2_score, np.mean, mode="regression")
]
metric = [dc.metrics.Metric(dc.metrics.pearson_r2_score, mode="regression")]

# Batch size of models
batch_size = 32
Loading