Commit 7c5bcff0 authored by Bharath Ramsundar's avatar Bharath Ramsundar
Browse files

Added in keras layers and more tests.

parent d984056d
Loading
Loading
Loading
Loading
+143 −0
Original line number Diff line number Diff line
"""
Convenience classes for assembling graph models.
"""
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals

__author__ = "Han Altae-Tran and Bharath Ramsundar"
__copyright__ = "Copyright 2016, Stanford University"
__license__ = "GPL"


from deepchem.models.tf_keras_models.keras_layers import GraphGather
from deepchem.models.tf_keras_models.containers import GraphContainer
from deepchem.models.tf_keras_models.containers import SupportGraphContainer
from deepchem.models.tf_keras_models.graph_topology import GraphTopology

class SequentialGraphModel(object):
  def __init__(self, n_atoms, n_feat, batch_size):
    #super(SequentialGraphModel, self).__init__()
    self.batch_size = batch_size
    # Create graph topology and x
    self.graph_topology = GraphTopology(n_atoms, n_feat, self.batch_size)
    self.output = self.graph_topology.get_nodes()

    self.layers = []  # Keep track of the layers

  def add(self, layer):
    """Adds a new layer to model."""
    # Update new value of x
    if type(layer).__name__ in ['GraphConv', 'GraphGather', 'GraphPool']:
      if (len(self.layers) > 0 and hasattr(self.layers[-1], "__name__")):
        assert (self.layers[-1].__name__ != "GraphGather",
                'Cannot use GraphConv or GraphGather layers after a GraphGather')
          
      self.output = layer(
          [self.output] + self.graph_topology.get_topology_placeholders())
    else:
      self.output = layer(self.output)

    # Add layer to the layer list
    self.layers.append(layer)

  '''
  def graph_gather(self, activation='linear'):
    gather = GraphGather(self.batch_size, activation=activation)

    self.layers.append(gather)
    
    self.output = gather(
        [self.output] + self.graph_topology.get_topology_placeholders())
  '''
    
  def return_container(self, sess):
    return GraphContainer(sess, input=self.return_inputs(),
                          output=self.return_outputs(),
                          graph_topology=self.graph_topology)
  
  def return_outputs(self):
    return self.output

  def return_inputs(self):
    return self.graph_topology.get_input_placeholders()

  def get_layer(self, layer_id):
    return self.layers[layer_id]

class SequentialSupportGraphModel(object):
  def __init__(self, n_atom, n_feat, test_batch_size, support_batch_size):

    self.test_batch_size = test_batch_size
    self.support_batch_size = support_batch_size

    # Create graph topology and x
    self.test_graph_topology = GraphTopology(
        n_atom, n_feat, test_batch_size, name='test')
    self.support_graph_topology = GraphTopology(
        n_atom, n_feat, support_batch_size, name='support')
    self.test = self.test_graph_topology.get_nodes()
    self.support = self.support_graph_topology.get_nodes()

    # Keep track of the layers
    self.layers = []  
    # Whether or not we have used the GraphGather layer yet
    self.bool_pre_gather = True  

  def add(self, layer):
    # Add layer to the layer list
    self.layers.append(layer)
    ############################################################# DEBUG
    print("SequentialSupportGraphModel.add()")
    print(layer)
    ############################################################# DEBUG

    # Update new value of x
    if type(layer).__name__ in ['GraphConv', 'GraphGather', 'GraphPool']:
      assert (self.bool_pre_gather,
              'Cannot use GraphConv or GraphGather layers after a GraphGather')
          
      self.test = layer([self.test] + self.test_graph_topology.topology)
      self.support = layer([self.support] + self.support_graph_topology.topology)
    else:
      self.test = layer(self.test)
      self.support = layer(self.support)

    if type(layer).__name__ == 'GraphGather':
      self.bool_pre_gather = False  # Set flag to stop adding topology

  def join(self, layer, swap=False):
    """Joins test and support to a two input two output layer"""
    self.layers.append(layer)
    if not swap:
        self.test, self.support = layer([self.test, self.support])
    else:
        self.support, self.test = layer([self.support, self.test])
  
  def graph_gather(self, activation='linear'):
    gather1 = GraphGather(self.test_batch_size, activation=activation)
    gather2 = GraphGather(self.support_batch_size, activation=activation)

    self.layers.append(gather1)
    self.layers.append(gather2)
    
    self.test = gather1([self.test] + self.test_graph_topology.topology)
    self.support = gather2([self.support] + self.support_graph_topology.topology)
    
    self.bool_pre_gather = False

  def return_container(self, sess):
    return SupportGraphContainer(
        sess, input=self.return_inputs(),
        output=self.return_outputs(), 
        graph_topology_test=self.test_graph_topology,
        graph_topology_support=self.support_graph_topology)
  
  def return_outputs(self):
    return [self.test] + [self.support]

  def return_inputs(self):
    return self.test_graph_topology.get_inputs() + self.support_graph_topology.get_inputs()

  def get_layer(self, layer_id):
    return self.layers[layer_id]
+27 −21
Original line number Diff line number Diff line
@@ -52,13 +52,11 @@ class GraphTopology(object):
    self.name = name
    self.max_deg = max_deg
    self.min_deg = min_deg
    self.init_keras_placeholders()

  def init_keras_placeholders(self):
    self.nodes_placeholder = Input(
    self.atom_features_placeholder = Input(
        tensor=K.placeholder(
            shape=(None, self.n_feat), dtype='float32',
            name=self.name+'_nodes'))
            name=self.name+'_atom_features'))
    self.deg_adj_lists_placeholders = [
        Input(tensor=K.placeholder(
          shape=(None, deg), dtype='int32', name=self.name+'_deg_adj'+str(deg)))
@@ -74,39 +72,46 @@ class GraphTopology(object):

    # Define the list of tensors to be used as topology
    self.topology = [self.deg_slice_placeholder, self.membership_placeholder]
    self.topology.extend(self.deg_adj_lists_placeholders)
    self.topology += self.deg_adj_lists_placeholders

    self.inputs = [self.nodes_placeholder]
    self.inputs.extend(self.topology)
    self.inputs = [self.atom_features_placeholder]
    self.inputs += self.topology

  def get_inputs(self):
  def get_input_placeholders(self):
    """All placeholders.

    Contains atom_features placeholder and topology placeholders.
    """
    return self.inputs

  def get_topology(self):
  def get_topology_placeholders(self):
    """Returns topology placeholders

    Consists of deg_slice_placeholder, membership_placeholder, and the
    deg_adj_list_placeholders.
    """
    return self.topology

  def get_batch_size(self):
    return self.batch_size

  def get_nodes(self):
    return self.nodes_placeholder
  def get_atom_features_placeholder(self):
    return self.atom_features_placeholder

  def get_deg_adj_lists(self):
  def get_deg_adjacency_lists_placeholders(self):
    return self.deg_adj_lists_placeholders

  def get_deg_slice(self):
  def get_deg_slice_placeholder(self):
    return self.deg_slice_placeholder

  def get_membership(self):
  def get_membership_placeholder(self):
    return self.membership_placeholder

  # TODO(rbharath): It's still not clear to me that this should live alone like
  # this... It's awkward to separate out part of the batch construction this
  # way.
  def batch_to_feed_dict(self, batch):
    """Converts the current batch into a feed_dict used by tensorflow.
    """Converts the current batch of mol_graphs into tensorflow feed_dict.

    Assigns the graph information in batch to the placeholders tensors
    Assigns the graph information in array of ConvMol objects to the
    placeholders tensors

    params
    ------
@@ -120,8 +125,7 @@ class GraphTopology(object):
    """
    # Merge mol conv objects
    batch = ConvMol.agglomerate_mols(batch)

    atoms = batch.nodes
    atoms = batch.get_atom_features()
    deg_adj_lists = [batch.deg_adj_lists[deg]
                     for deg in range(1, self.max_deg+1)]

@@ -132,6 +136,7 @@ class GraphTopology(object):
                  self.membership_placeholder : batch.membership}
    return merge_dicts([atoms_dict, deg_adj_dict])

'''
def extract_topology(x):
  # Extracts the topology tensors from x
  topology = x[1::]
@@ -149,3 +154,4 @@ def extract_nodes(x):

def extract_membership(x):
  return x[2]
'''
+681 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ from deepchem.models.tf_keras_models.graph_topology import GraphTopology

class TestContainers(test_util.TensorFlowTestCase):
  """
  Test Container usage."
  Test Container usage.
  """
  def setUp(self):
    super(TestContainers, self).setUp()
+32 −0
Original line number Diff line number Diff line
"""
Testing construction of graph models.
"""
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals

__author__ = "Han Altae-Tran and Bharath Ramsundar"
__copyright__ = "Copyright 2016, Stanford University"
__license__ = "GPL"

import unittest
from tensorflow.python.framework import test_util
from deepchem.models.tf_keras_models.containers import GraphContainer
from deepchem.models.tf_keras_models.graph_topology import GraphTopology
from deepchem.models.tf_keras_models.graph_models import SequentialGraphModel

class TestGraphModels(test_util.TensorFlowTestCase):
  """
  Test Container usage.
  """
  def setUp(self):
    super(TestGraphModels, self).setUp()
    self.root = '/tmp'

  def test_sequential_graph_model(self):
    """Simple test that SequentialGraphModel can be initialized."""
    n_atoms = 5
    n_atom_feat = 10
    batch_size = 3
    graph_model = SequentialGraphModel(n_atoms, n_feat, batch_size)
    assert len(graph_model.layers) == 0
Loading