├── .gitattributes ├── LICENSE ├── README.md ├── docs ├── Filters │ └── graph_conv_filters.md ├── Layers │ ├── Attention │ │ └── graph_attention_layer.md │ ├── Convolution │ │ └── graph_conv_layer.md │ ├── Graph Capsule Neural Network │ │ └── graph_capsule_cnn.md │ ├── Graph Neural Network │ │ └── graph_neural_networks.md │ └── Recurrent │ │ └── graph_conv_recurrent_layer.md ├── about.md └── index.md ├── examples ├── data │ ├── A_edge_matrices_mutag.csv │ ├── A_mutag.csv │ ├── G_enzymes.mat │ ├── G_enzymes_node_labels.mat │ ├── G_imdb_binary.mat │ ├── G_mutag.mat │ ├── G_nci1.mat │ ├── G_nci109.mat │ ├── G_nci1_node_labels.mat │ ├── G_ptc.mat │ ├── X_mutag.csv │ ├── Y_enzymes.mat │ ├── Y_imdb_binary.mat │ ├── Y_mutag.csv │ ├── Y_mutag.mat │ ├── Y_nci1.mat │ ├── Y_nci109.mat │ ├── Y_ptc.mat │ └── cora │ │ ├── README │ │ ├── cora.cites │ │ └── cora.content ├── gcnn_node_classification_example.py ├── gcnn_node_classification_with_edge_features_example.py ├── graph_attention_cnn_node_classification_example.py ├── multi_gcnn_graph_classification_example.py ├── multi_gcnn_graph_classification_with_edge_features_example.py ├── multi_graph_attention_cnn_graph_classification_example.py └── utils.py ├── keras_dgl ├── __init__.py └── layers │ ├── __init__.py │ ├── graph_attention_cnn_layer.py │ ├── graph_cnn_layer.py │ ├── graph_convolutional_recurrent_layer.py │ ├── graph_ops.py │ ├── multi_graph_attention_cnn_layer.py │ └── multi_graph_cnn_layer.py ├── mkdocs.yml └── site ├── 404.html ├── Filters └── graph_conv_filters │ └── index.html ├── Layers ├── Attention │ └── graph_attention_layer │ │ └── index.html ├── Convolution │ └── graph_conv_layer │ │ └── index.html ├── Graph Capsule Neural Network │ └── graph_capsule_cnn │ │ └── index.html ├── Graph Neural Network │ └── graph_neural_networks │ │ └── index.html └── Recurrent │ └── graph_conv_recurrent_layer │ └── index.html ├── about └── index.html ├── css ├── highlight.css ├── highlight_original.css ├── theme.css ├── theme_extra.css ├── theme_extra_original.css └── theme_original.css ├── fonts ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── img ├── favicon.ico └── favicon_orginal.ico ├── index.html ├── js ├── highlight.pack.js ├── jquery-2.1.1.min.js ├── modernizr-2.8.3.min.js └── theme.js ├── search.html ├── search ├── lunr.min.js ├── mustache.min.js ├── require.js ├── search-results-template.mustache ├── search.js ├── search_index.json └── text.js └── sitemap.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Saurabh Verma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Keras Deep Learning on Graphs (Keras-DGL) 2 | 3 | The aim of this keras extension is to provide Sequential and Functional API for performing deep learning tasks on graphs. Specifically, Keras-DGL provides implementation for these particular type of layers, 4 | 5 | * Graph Convolutional Neural Networks (GraphCNN). 6 | * Graph Attention Convolutional Neural Networks (GraphAttentionCNN). 7 | * Graph Convolutional Recurrent Neural Networks (GraphConvLSTM). 8 | * Graph Capsule Convolutional Recurrent Neural Networks (GraphCapsuleCNN) TBD. 9 | * Graph Message Passing Neural Networks (GraphNeuralNetworks) TBD. 10 | * Keras-DGL also contains implementation of various graph convolutional filters TBD. 11 | 12 | Read the documentation: 13 | -------------------------------------------------------------------------------- /docs/Filters/graph_conv_filters.md: -------------------------------------------------------------------------------- 1 | #### Standard Polynomial 2 | 3 | ```python 4 | standard_poly(A, poly_degree=3) 5 | ``` 6 | Computes Standard Polynomial function. Current implementation complexity is $O(N^2)$. 7 | 8 | 9 | __Inputs__: 10 | 11 | * __A__ : 2D Tensor, graph adjacency matrix or (normalized) Laplacian. 12 | * __poly_degree__: Integer, polynomial degree (default=1). 13 | 14 | __Outputs__: 15 | 16 | * 3D Tensor, containing standard polynomial powers of graph adjacency matrix or Laplacian. 17 | 18 | ---- 19 | 20 | #### Chebyshev Polynomial 21 | 22 | ```python 23 | chebyshev_poly(A, poly_degree=3) 24 | ``` 25 | Computes Chebyshev Polynomial function. Current implementation complexity is $O(N^2)$. 26 | 27 | __Inputs__: 28 | 29 | * __A__ : 2D Tensor, graph adjacency matrix or (normalized) Laplacian. 30 | * __poly_degree__: Integer, polynomial degree (default=1). 31 | 32 | __Outputs__: 33 | 34 | * 3D Tensor, containing chebyshev polynomial powers of graph adjacency matrix or Laplacian. 35 | 36 | __References__: Defferrard, Michaël, Xavier Bresson, and Pierre Vandergheynst. "Convolutional neural networks on graphs with fast localized spectral filtering." In Advances in Neural Information Processing Systems, pp. 3844-3852. 2016. 37 | 38 | ---- 39 | 40 | #### Random Walk Polynomial 41 | 42 | ```python 43 | chebyshev_poly(A, poly_degree=3) 44 | ``` 45 | Computes Random Walk Polynomial function. Current implementation complexity is $O(N^2)$. 46 | 47 | __Inputs__: 48 | 49 | * __A__ : 2D Tensor, graph adjacency matrix or (normalized) Laplacian. 50 | * __poly_degree__: Integer, polynomial degree (default=1). 51 | 52 | __Outputs__: 53 | 54 | * 3D Tensor, containing chebyshev polynomial powers of graph adjacency matrix or Laplacian. 55 | 56 | ---- 57 | 58 | 59 | 60 | #### Cayley Polynomial 61 | 62 | ```python 63 | cayley_poly(A, poly_degree=3) 64 | ``` 65 | Computes Cayley Polynomial function. Current implementation complexity is $O(N^3)$. 66 | 67 | __Inputs__: 68 | 69 | * __A__ : 2D Tensor, graph adjacency matrix or (normalized) Laplacian. 70 | * __poly_degree__: Integer, polynomial degree (default=1). 71 | 72 | __Outputs__: 73 | 74 | * 3D Tensor, containing cayley polynomial powers of graph adjacency matrix or Laplacian. 75 | 76 | __References__: Levie, Ron, Federico Monti, Xavier Bresson, and Michael M. Bronstein. "Cayleynets: Graph convolutional neural networks with complex rational spectral filters." arXiv preprint arXiv:1705.07664 (2017). 77 | 78 | ---- 79 | 80 | #### Combine Polynomial 81 | 82 | ```python 83 | combine_poly(A, B, poly_degree=3) 84 | ``` 85 | Computes combination of polynomial function. 86 | 87 | __Inputs__: 88 | 89 | * __A__ : 2D Tensor, graph adjacency or (normalized) Laplacian or cayley matrix. 90 | * __B__ : 2D Tensor, graph adjacency matrix or (normalized) Laplacian or cayley matrix. 91 | * __poly_degree__: Integer, polynomial degree (default=1). 92 | 93 | __Outputs__: 94 | 95 | * 3D Tensor, containing combine polynomial powers of graph adjacency or Laplacian or cayley matrix. 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /docs/Layers/Attention/graph_attention_layer.md: -------------------------------------------------------------------------------- 1 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/keras_dgl/layers/graph_attention_cnn_layer.py#L10) 2 | ## GraphAttentionCNN 3 | 4 | ```python 5 | GraphAttentionCNN(output_dim, adjacency_matrix, num_filters=None, graph_conv_filters=None, activation=None, use_bias=False, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 6 | ``` 7 | 8 | GraphAttention layer assumes a fixed input graph structure which is passed as a layer argument. As a result, the input order of graph nodes are fixed for the model and should match the nodes order in inputs. Also, graph structure can not be changed once the model is compiled. This choice enable us to use Keras Sequential API but comes with some constraints (for instance shuffling is not possible anymore in-or-after each epoch). See further [remarks below](http://127.0.0.1:8000/Layers/Convolution/graph_conv_layer/#remarks) about this specific choice.
9 | 10 | 11 | __Arguments__ 12 | 13 | - __output_dim__: Positive integer, dimensionality of each graph node feature output space (or also referred dimension of graph node embedding). 14 | - __adjacency_matrix__: input as a 2D tensor with shape: `(num_graph_nodes, num_graph_nodes)` with __diagonal values__ equal to 1.
15 | - __num_filters__: None or Positive integer, number of graph filters used for constructing __graph_conv_filters__ input. 16 | - __graph_conv_filters__: None or input as a 2D tensor with shape: `(num_filters*num_graph_nodes, num_graph_nodes)`
17 | `num_filters` is different number of graph convolution filters to be applied on graph. For instance `num_filters` could be power of graph Laplacian. Here list of graph convolutional matrices are stacked along second-last axis.
18 | - __activation__: Activation function to use 19 | (see [activations](../activations.md)). 20 | If you don't specify anything, no activation is applied 21 | (ie. "linear" activation: `a(x) = x`). 22 | - __use_bias__: Boolean, whether the layer uses a bias vector (recommended setting is False for this layer). 23 | - __kernel_initializer__: Initializer for the `kernel` weights matrix 24 | (see [initializers](../initializers.md)). 25 | - __bias_initializer__: Initializer for the bias vector 26 | (see [initializers](../initializers.md)). 27 | - __kernel_regularizer__: Regularizer function applied to 28 | the `kernel` weights matrix 29 | (see [regularizer](../regularizers.md)). 30 | - __bias_regularizer__: Regularizer function applied to the bias vector 31 | (see [regularizer](../regularizers.md)). 32 | - __activity_regularizer__: Regularizer function applied to 33 | the output of the layer (its "activation"). 34 | (see [regularizer](../regularizers.md)). 35 | - __kernel_constraint__: Constraint function applied to the kernel matrix 36 | (see [constraints](https://keras.io/constraints/)). 37 | - __bias_constraint__: Constraint function applied to the bias vector 38 | (see [constraints](https://keras.io/constraints/)). 39 | 40 | 41 | 42 | __Input shapes__ 43 | 44 | * 2D tensor with shape: `(num_graph_nodes, input_dim)` representing graph node input feature matrix.
45 | 46 | 47 | __Output shape__ 48 | 49 | * 2D tensor with shape: `(num_graph_nodes, output_dim)` representing convoluted output graph node embedding (or signal) matrix.
50 | 51 | 52 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/examples/graph_attention_cnn_node_classification_example.py) 53 | ####__Example__: Graph Semi-Supervised Learning (or Node Label Classification) 54 | 55 | 56 | ```python 57 | # A complete example of applying GraphCNN layer for performing node label classification. 58 | 59 | model = Sequential() 60 | model.add(Dropout(0.6, input_shape=(X.shape[1],))) 61 | model.add(GraphAttentionCNN(8, 1, A, num_attention_heads=8, attention_heads_reduction='concat', attention_dropout=0.6, activation='elu', kernel_regularizer=l2(5e-4))) 62 | model.add(Dropout(0.6)) 63 | model.add(GraphAttentionCNN(Y.shape[1], 1, A, num_attention_heads=1, attention_heads_reduction='average', attention_dropout=0.6, activation='elu', kernel_regularizer=l2(5e-4))) 64 | model.add(Activation('softmax')) 65 | model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=5e-3), metrics=['accuracy']) 66 | 67 | NB_EPOCH = 1000 68 | 69 | for epoch in range(1, NB_EPOCH + 1): 70 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=1, shuffle=False, verbose=0) 71 | Y_pred = model.predict(X, batch_size=A.shape[0]) 72 | _, train_acc = evaluate_preds(Y_pred, [Y_train], [train_idx]) 73 | _, test_acc = evaluate_preds(Y_pred, [Y_test], [test_idx]) 74 | print("Epoch: {:04d}".format(epoch), "train_acc= {:.4f}".format(train_acc[0]), 75 | "test_acc= {:.4f}".format(test_acc[0])) 76 | 77 | ``` 78 | ---- 79 | 80 | 81 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/keras_dgl/layers/multi_graph_attention_cnn_layer.py#L11) 82 | ## MultiGraphAttentionCNN 83 | 84 | ```python 85 | MutliGraphCNN(output_dim, num_filters, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 86 | ``` 87 | 88 | MutliGraphCNN assumes that the number of nodes for each graph in the dataset is same. For graph with arbitrary size, one can simply append appropriate zero rows or columns in adjacency matrix (and node feature matrix) based on max graph size in the dataset to achieve this uniformity. 89 | 90 | __Arguments__ 91 | 92 | - __output_dim__: Positive integer, dimensionality of each graph node feature output space (or also referred dimension of graph node embedding). 93 | - __num_filters__: Positive integer, number of graph filters used for constructing __graph_conv_filters__ input. 94 | - __activation__: Activation function to use 95 | (see [activations](../activations.md)). 96 | If you don't specify anything, no activation is applied 97 | (ie. "linear" activation: `a(x) = x`). 98 | - __use_bias__: Boolean, whether the layer uses a bias vector. 99 | - __kernel_initializer__: Initializer for the `kernel` weights matrix 100 | (see [initializers](../initializers.md)). 101 | - __bias_initializer__: Initializer for the bias vector 102 | (see [initializers](../initializers.md)). 103 | - __kernel_regularizer__: Regularizer function applied to 104 | the `kernel` weights matrix 105 | (see [regularizer](../regularizers.md)). 106 | - __bias_regularizer__: Regularizer function applied to the bias vector 107 | (see [regularizer](../regularizers.md)). 108 | - __activity_regularizer__: Regularizer function applied to 109 | the output of the layer (its "activation"). 110 | (see [regularizer](../regularizers.md)). 111 | - __kernel_constraint__: Constraint function applied to the kernel matrix 112 | (see [constraints](https://keras.io/constraints/)). 113 | - __bias_constraint__: Constraint function applied to the bias vector 114 | (see [constraints](https://keras.io/constraints/)). 115 | 116 | __Input shapes__ 117 | 118 | * __graph node feature matrix__ input as a 3D tensor with shape: `(batch_size, num_graph_nodes, input_dim)` corresponding to graph node input feature matrix for each graph.
119 | * __graph_conv_filters__ input as a 3D tensor with shape: `(batch_size, num_filters*num_graph_nodes, num_graph_nodes)`
120 | `num_filters` is different number of graph convolution filters to be applied on graph. For instance `num_filters` could be power of graph Laplacian.
121 | 122 | __Output shape__ 123 | 124 | * 3D tensor with shape: `(batch_size, num_graph_nodes, output_dim)` representing convoluted output graph node embedding matrix for each graph in batch size.
125 | 126 | 127 | 128 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/examples/multi_graph_attention_cnn_graph_classification_example.py) 129 | ###__Example 3__: Graph Classification 130 | 131 | 132 | ```python 133 | # See multi_graph_attention_cnn_graph_classification_example.py for complete code. 134 | 135 | from keras_dgl.layers import MultiAttentionGraphCNN 136 | 137 | X_input = Input(shape=(X.shape[1], X.shape[2])) 138 | A_input = Input(shape=(A.shape[1], A.shape[2])) 139 | graph_conv_filters_input = Input(shape=(graph_conv_filters.shape[1], graph_conv_filters.shape[2])) 140 | 141 | output = MultiGraphAttentionCNN(100, num_filters=num_filters, num_attention_heads=2, attention_combine='concat', attention_dropout=0.5, activation='elu', kernel_regularizer=l2(5e-4))([X_input, A_input, graph_conv_filters_input]) 142 | output = Dropout(0.2)(output) 143 | output = MultiGraphAttentionCNN(100, num_filters=num_filters, num_attention_heads=1, attention_combine='average', attention_dropout=0.5, activation='elu', kernel_regularizer=l2(5e-4))([output, A_input, graph_conv_filters_input]) 144 | output = Dropout(0.2)(output) 145 | output = Lambda(lambda x: K.mean(x, axis=1))(output) # adding a node invariant layer to make sure output does not depends upon the node order in a graph. 146 | output = Dense(Y.shape[1], activation='elu')(output) 147 | output = Activation('softmax')(output) 148 | 149 | nb_epochs = 500 150 | batch_size = 169 151 | 152 | model = Model(inputs=[X_input, A_input, graph_conv_filters_input], outputs=output) 153 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 154 | model.fit([X, A, graph_conv_filters], Y, batch_size=batch_size, validation_split=0.1, epochs=nb_epochs, shuffle=True, verbose=1) 155 | ``` 156 | 157 | 158 | ---- 159 | -------------------------------------------------------------------------------- /docs/Layers/Convolution/graph_conv_layer.md: -------------------------------------------------------------------------------- 1 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/keras_dgl/layers/graph_cnn_layer.py#L9) 2 | ## GraphCNN 3 | 4 | ```python 5 | GraphCNN(output_dim, num_filters, graph_conv_filters, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 6 | ``` 7 | 8 | GraphCNN layer assumes a fixed input graph structure which is passed as a layer argument. As a result, the input order of graph nodes are fixed for the model and should match the nodes order in inputs. Also, graph structure can not be changed once the model is compiled. This choice enable us to use Keras Sequential API but comes with some constraints (for instance shuffling is not possible anymore in-or-after each epoch). See further [remarks below](http://127.0.0.1:8000/Layers/Convolution/graph_conv_layer/#remarks) about this specific choice.
9 | 10 | 11 | __Arguments__ 12 | 13 | - __output_dim__: Positive integer, dimensionality of each graph node feature output space (or also referred dimension of graph node embedding). 14 | - __num_filters__: Positive integer, number of graph filters used for constructing __graph_conv_filters__ input. 15 | - __graph_conv_filters__ input as a 2D tensor with shape: `(num_filters*num_graph_nodes, num_graph_nodes)`
16 | `num_filters` is different number of graph convolution filters to be applied on graph. For instance `num_filters` could be power of graph Laplacian. Here list of graph convolutional matrices are stacked along second-last axis.
17 | - __activation__: Activation function to use 18 | (see [activations](../activations.md)). 19 | If you don't specify anything, no activation is applied 20 | (ie. "linear" activation: `a(x) = x`). 21 | - __use_bias__: Boolean, whether the layer uses a bias vector. 22 | - __kernel_initializer__: Initializer for the `kernel` weights matrix 23 | (see [initializers](../initializers.md)). 24 | - __bias_initializer__: Initializer for the bias vector 25 | (see [initializers](../initializers.md)). 26 | - __kernel_regularizer__: Regularizer function applied to 27 | the `kernel` weights matrix 28 | (see [regularizer](../regularizers.md)). 29 | - __bias_regularizer__: Regularizer function applied to the bias vector 30 | (see [regularizer](../regularizers.md)). 31 | - __activity_regularizer__: Regularizer function applied to 32 | the output of the layer (its "activation"). 33 | (see [regularizer](../regularizers.md)). 34 | - __kernel_constraint__: Constraint function applied to the kernel matrix 35 | (see [constraints](https://keras.io/constraints/)). 36 | - __bias_constraint__: Constraint function applied to the bias vector 37 | (see [constraints](https://keras.io/constraints/)). 38 | 39 | 40 | 41 | __Input shapes__ 42 | 43 | * 2D tensor with shape: `(num_graph_nodes, input_dim)` representing graph node input feature matrix.
44 | 45 | 46 | __Output shape__ 47 | 48 | * 2D tensor with shape: `(num_graph_nodes, output_dim)` representing convoluted output graph node embedding (or signal) matrix.
49 | 50 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/examples/gcnn_node_classification_example.py) 51 | ####__Example 1__: Graph Semi-Supervised Learning (or Node Classification) 52 | 53 | 54 | ```python 55 | # A sample code for applying GraphCNN layer to perform node classification. 56 | # See examples/gcnn_node_classification_example.py for complete code. 57 | 58 | from keras_dgl.layers import GraphCNN 59 | 60 | model = Sequential() 61 | model.add(GraphCNN(16, 2, graph_conv_filters, input_shape=(X.shape[1],), activation='elu', kernel_regularizer=l2(5e-4))) 62 | model.add(Dropout(0.2)) 63 | model.add(GraphCNN(Y.shape[1], 2, graph_conv_filters, kernel_regularizer=l2(5e-4))) 64 | model.add(Activation('softmax')) 65 | 66 | model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.01), metrics=['acc']) 67 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=500, shuffle=False, verbose=0) 68 | ``` 69 | 70 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/examples/gcnn_node_classification_with_edge_features_example.py) 71 | ###__Example 2__: Graph Edge Convolution for Node Classification 72 | 73 | 74 | ```python 75 | # A sample code for applying GraphCNN layer while taking edge features into account to perform node label classification. 76 | # For edge convolution all we need is to provide a graph_conv_filters which contains (stack) adjacency matrices corresponding to each edge features. See note below on example2. 77 | # See graphcnn_example2.py for complete code. 78 | 79 | from keras_dgl.layers import GraphCNN 80 | 81 | model = Sequential() 82 | model.add(GraphCNN(16, 2, graph_conv_filters, activation='elu')) 83 | model.add(Dropout(0.2)) 84 | model.add(GraphCNN(Y.shape[1], 2, graph_conv_filters)) 85 | model.add(Activation('softmax')) 86 | 87 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 88 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=500, shuffle=False) 89 | ``` 90 | 91 | Note on Example 2: Equation $1$ in the paper (see reference [3]) can be written as $Y=\sum\limits_{s=0}^{S}A^{(s)}X\theta^{(s)}$. This is defined as graph edge convolution. All we have to do is stack $A^{(s)}$ and feed to GraphCNN layer to perform graph edge convolution. 92 | 93 | 94 | ---- 95 | 96 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/keras_dgl/layers/multi_graph_cnn_layer.py#L9) 97 | ## MutliGraphCNN 98 | 99 | ```python 100 | MutliGraphCNN(output_dim, num_filters, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 101 | ``` 102 | 103 | MutliGraphCNN assumes that the number of nodes for each graph in the dataset is same. For graph with arbitrary size, one can simply append appropriate zero rows or columns in adjacency matrix (and node feature matrix) based on max graph size in the dataset to achieve this uniformity. 104 | 105 | __Arguments__ 106 | 107 | - __output_dim__: Positive integer, dimensionality of each graph node feature output space (or also referred dimension of graph node embedding). 108 | - __num_filters__: Positive integer, number of graph filters used for constructing __graph_conv_filters__ input. 109 | - __activation__: Activation function to use 110 | (see [activations](../activations.md)). 111 | If you don't specify anything, no activation is applied 112 | (ie. "linear" activation: `a(x) = x`). 113 | - __use_bias__: Boolean, whether the layer uses a bias vector. 114 | - __kernel_initializer__: Initializer for the `kernel` weights matrix 115 | (see [initializers](../initializers.md)). 116 | - __bias_initializer__: Initializer for the bias vector 117 | (see [initializers](../initializers.md)). 118 | - __kernel_regularizer__: Regularizer function applied to 119 | the `kernel` weights matrix 120 | (see [regularizer](../regularizers.md)). 121 | - __bias_regularizer__: Regularizer function applied to the bias vector 122 | (see [regularizer](../regularizers.md)). 123 | - __activity_regularizer__: Regularizer function applied to 124 | the output of the layer (its "activation"). 125 | (see [regularizer](../regularizers.md)). 126 | - __kernel_constraint__: Constraint function applied to the kernel matrix 127 | (see [constraints](https://keras.io/constraints/)). 128 | - __bias_constraint__: Constraint function applied to the bias vector 129 | (see [constraints](https://keras.io/constraints/)). 130 | 131 | __Input shapes__ 132 | 133 | * __graph node feature matrix__ input as a 3D tensor with shape: `(batch_size, num_graph_nodes, input_dim)` corresponding to graph node input feature matrix for each graph.
134 | * __graph_conv_filters__ input as a 3D tensor with shape: `(batch_size, num_filters*num_graph_nodes, num_graph_nodes)`
135 | `num_filters` is different number of graph convolution filters to be applied on graph. For instance `num_filters` could be power of graph Laplacian.
136 | 137 | __Output shape__ 138 | 139 | * 3D tensor with shape: `(batch_size, num_graph_nodes, output_dim)` representing convoluted output graph node embedding matrix for each graph in batch size.
140 | 141 | 142 | 143 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/examples/multi_gcnn_graph_classification_example.py) 144 | ###__Example 3__: Graph Classification 145 | 146 | 147 | ```python 148 | # See multi_gcnn_graph_classification_example.py for complete code. 149 | 150 | from keras_dgl.layers import MultiGraphCNN 151 | 152 | X_shape = Input(shape=(X.shape[1], X.shape[2])) 153 | graph_conv_filters_shape = Input(shape=(graph_conv_filters.shape[1], graph_conv_filters.shape[2])) 154 | 155 | output = MultiGraphCNN(100, num_filters, activation='elu')([X_shape, graph_conv_filters_shape]) 156 | output = Dropout(0.2)(output) 157 | output = MultiGraphCNN(100, num_filters, activation='elu')([output, graph_conv_filters_shape]) 158 | output = Dropout(0.2)(output) 159 | output = Lambda(lambda x: K.mean(x, axis=1))(output) # adding a node invariant layer to make sure output does not depends upon the node order in a graph. 160 | output = Dense(Y.shape[1])(output) 161 | output = Activation('softmax')(output) 162 | 163 | nb_epochs = 200 164 | batch_size = 169 165 | 166 | model = Model(inputs=[X_shape, graph_conv_filters_shape], outputs=output) 167 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 168 | model.fit([X, graph_conv_filters], Y, batch_size=batch_size, validation_split=0.1, epochs=nb_epochs, shuffle=True, verbose=1) 169 | ``` 170 | 171 | 172 | 173 | ---- 174 | 175 | #Remarks 176 | 177 | __Why pass graph_conv_filters as a layer argument and not as an input in GraphCNN?__
178 | The problem lies with keras multi-input functional API. It requires --- all input arrays (x) should have the same number of samples i.e., all inputs first dimension axis should be same. In special cases the first dimension of inputs could be same, for example check out Kipf .et.al. keras implementation [[source]](https://github.com/tkipf/keras-gcn/blob/master/kegra/train.py). But in cases such as a graph recurrent neural networks this does not hold true. 179 | 180 | 181 | __Why pass graph_conv_filters as 2D tensor of this specific format?__
182 | Passing graph_conv_filters input as a 2D tensor with shape: `(K*num_graph_nodes, num_graph_nodes)` cut down few number of tensor computation operations. 183 | 184 | __References__:
185 | [1] Kipf, Thomas N., and Max Welling. "Semi-supervised classification with graph convolutional networks." arXiv preprint arXiv:1609.02907 (2016).
186 | [2] Defferrard, Michaël, Xavier Bresson, and Pierre Vandergheynst. "Convolutional neural networks on graphs with fast localized spectral filtering." In Advances in Neural Information Processing Systems, pp. 3844-3852. 2016.
187 | [3] Simonovsky, Martin, and Nikos Komodakis. "Dynamic edge-conditioned filters in convolutional neural networks on graphs." In Proc. CVPR. 2017.
188 | -------------------------------------------------------------------------------- /docs/Layers/Graph Capsule Neural Network/graph_capsule_cnn.md: -------------------------------------------------------------------------------- 1 | TBD -------------------------------------------------------------------------------- /docs/Layers/Graph Neural Network/graph_neural_networks.md: -------------------------------------------------------------------------------- 1 | TBD -------------------------------------------------------------------------------- /docs/Layers/Recurrent/graph_conv_recurrent_layer.md: -------------------------------------------------------------------------------- 1 | [[source]](https://github.com/vermaMachineLearning/keras-deep-graph-learning/blob/master/keras_dgl/layers/graph_convolutional_recurrent_layer.py#L116) 2 | ### GraphConvLSTM 3 | 4 | ```python 5 | GraphConvLSTM(output_dim, graph_conv_filters, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 6 | ``` 7 | 8 | 1D convolution layer (e.g. temporal convolution). 9 | 10 | This layer creates a convolution kernel that is convolved 11 | with the layer input over a single spatial (or temporal) dimension 12 | to produce a tensor of outputs. 13 | If `use_bias` is True, a bias vector is created and added to the outputs. 14 | Finally, if `activation` is not `None`, 15 | it is applied to the outputs as well. 16 | 17 | When using this layer as the first layer in a model, 18 | provide an `input_shape` argument 19 | (tuple of integers or `None`, e.g. 20 | `(10, 128)` for sequences of 10 vectors of 128-dimensional vectors, 21 | or `(None, 128)` for variable-length sequences of 128-dimensional vectors. 22 | 23 | __Arguments__ 24 | 25 | - __output_dim__: Positive integer, dimensionality of each graph node output space (or dimension of graph node embedding). 26 | - __graph_conv_filters__: 3D Tensor, the dimensionality of the output space 27 | (i.e. the number output of filters in the convolution). 28 | - __activation__: Activation function to use 29 | (see [activations](../activations.md)). 30 | If you don't specify anything, no activation is applied 31 | (ie. "linear" activation: `a(x) = x`). 32 | - __use_bias__: Boolean, whether the layer uses a bias vector. 33 | - __kernel_initializer__: Initializer for the `kernel` weights matrix 34 | (see [initializers](../initializers.md)). 35 | - __bias_initializer__: Initializer for the bias vector 36 | (see [initializers](../initializers.md)). 37 | - __kernel_regularizer__: Regularizer function applied to 38 | the `kernel` weights matrix 39 | (see [regularizer](../regularizers.md)). 40 | - __bias_regularizer__: Regularizer function applied to the bias vector 41 | (see [regularizer](../regularizers.md)). 42 | - __activity_regularizer__: Regularizer function applied to 43 | the output of the layer (its "activation"). 44 | (see [regularizer](../regularizers.md)). 45 | - __kernel_constraint__: Constraint function applied to the kernel matrix 46 | (see [constraints](https://keras.io/constraints/)). 47 | - __bias_constraint__: Constraint function applied to the bias vector 48 | (see [constraints](https://keras.io/constraints/)). 49 | 50 | __Input shapes__ 51 | 52 | * 4D tensor with shape: `(samples, timestep, num_graph_nodes, input_dim)`
53 | 54 | __Output shape__ 55 | 56 | * if `return_sequences`
57 | 4D tensor with shape: `(samples, timestep, num_graph_nodes, output_dim)`
58 | * else
59 | 4D tensor with shape: `(samples, num_graph_nodes, output_dim)`
-------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | ## Author 2 | 3 | Saurabh Verma, PhD Student at University of Minnesota Twin Cities. 4 | 5 | About me: 6 | 7 | **Acknowledgments**: Some part of the code has been borrowed from the original authors implementation and others. List of these authors are TBD. -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to Keras Deep Learning on Graphs (Keras-DGL) 2 | 3 | The aim of this keras extension is to provide Sequential and Functional API for performing deep learning tasks on graphs. Specifically, Keras-DGL provides implementation for these particular type of layers, 4 | 5 | * Graph Convolutional Neural Networks (GraphCNN). 6 | * Graph Attention Convolutional Neural Networks (GraphAttentionCNN). 7 | * Graph Convolutional Recurrent Neural Networks (GraphConvLSTM). 8 | * Graph Capsule Convolutional Recurrent Neural Networks (GraphCapsuleCNN) TBD. 9 | * Graph Message Passing Neural Networks (GraphNeuralNetworks) TBD. 10 | * Keras-DGL also contains implementation of various graph convolutional filters TBD. 11 | -------------------------------------------------------------------------------- /examples/data/G_enzymes.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_enzymes.mat -------------------------------------------------------------------------------- /examples/data/G_enzymes_node_labels.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_enzymes_node_labels.mat -------------------------------------------------------------------------------- /examples/data/G_imdb_binary.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_imdb_binary.mat -------------------------------------------------------------------------------- /examples/data/G_mutag.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_mutag.mat -------------------------------------------------------------------------------- /examples/data/G_nci1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_nci1.mat -------------------------------------------------------------------------------- /examples/data/G_nci109.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_nci109.mat -------------------------------------------------------------------------------- /examples/data/G_nci1_node_labels.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_nci1_node_labels.mat -------------------------------------------------------------------------------- /examples/data/G_ptc.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/G_ptc.mat -------------------------------------------------------------------------------- /examples/data/Y_enzymes.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_enzymes.mat -------------------------------------------------------------------------------- /examples/data/Y_imdb_binary.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_imdb_binary.mat -------------------------------------------------------------------------------- /examples/data/Y_mutag.csv: -------------------------------------------------------------------------------- 1 | 0,1 2 | 0,1 3 | 0,1 4 | 0,1 5 | 0,1 6 | 0,1 7 | 0,1 8 | 0,1 9 | 0,1 10 | 0,1 11 | 0,1 12 | 0,1 13 | 0,1 14 | 0,1 15 | 0,1 16 | 0,1 17 | 0,1 18 | 0,1 19 | 0,1 20 | 0,1 21 | 0,1 22 | 0,1 23 | 0,1 24 | 0,1 25 | 0,1 26 | 0,1 27 | 0,1 28 | 0,1 29 | 0,1 30 | 0,1 31 | 0,1 32 | 0,1 33 | 0,1 34 | 0,1 35 | 0,1 36 | 0,1 37 | 0,1 38 | 0,1 39 | 0,1 40 | 0,1 41 | 0,1 42 | 0,1 43 | 0,1 44 | 0,1 45 | 0,1 46 | 0,1 47 | 0,1 48 | 0,1 49 | 0,1 50 | 0,1 51 | 0,1 52 | 0,1 53 | 0,1 54 | 0,1 55 | 0,1 56 | 0,1 57 | 0,1 58 | 0,1 59 | 0,1 60 | 0,1 61 | 0,1 62 | 0,1 63 | 0,1 64 | 0,1 65 | 0,1 66 | 0,1 67 | 0,1 68 | 0,1 69 | 0,1 70 | 0,1 71 | 0,1 72 | 0,1 73 | 0,1 74 | 0,1 75 | 0,1 76 | 0,1 77 | 0,1 78 | 0,1 79 | 0,1 80 | 0,1 81 | 0,1 82 | 0,1 83 | 0,1 84 | 0,1 85 | 0,1 86 | 0,1 87 | 0,1 88 | 0,1 89 | 0,1 90 | 0,1 91 | 0,1 92 | 0,1 93 | 0,1 94 | 0,1 95 | 0,1 96 | 0,1 97 | 0,1 98 | 0,1 99 | 0,1 100 | 0,1 101 | 0,1 102 | 0,1 103 | 0,1 104 | 0,1 105 | 0,1 106 | 0,1 107 | 0,1 108 | 0,1 109 | 0,1 110 | 0,1 111 | 0,1 112 | 0,1 113 | 0,1 114 | 0,1 115 | 0,1 116 | 0,1 117 | 0,1 118 | 0,1 119 | 0,1 120 | 0,1 121 | 0,1 122 | 0,1 123 | 0,1 124 | 0,1 125 | 0,1 126 | 1,0 127 | 1,0 128 | 1,0 129 | 1,0 130 | 1,0 131 | 1,0 132 | 1,0 133 | 1,0 134 | 1,0 135 | 1,0 136 | 1,0 137 | 1,0 138 | 1,0 139 | 1,0 140 | 1,0 141 | 1,0 142 | 1,0 143 | 1,0 144 | 1,0 145 | 1,0 146 | 1,0 147 | 1,0 148 | 1,0 149 | 1,0 150 | 1,0 151 | 1,0 152 | 1,0 153 | 1,0 154 | 1,0 155 | 1,0 156 | 1,0 157 | 1,0 158 | 1,0 159 | 1,0 160 | 1,0 161 | 1,0 162 | 1,0 163 | 1,0 164 | 1,0 165 | 1,0 166 | 1,0 167 | 1,0 168 | 1,0 169 | 1,0 170 | 1,0 171 | 1,0 172 | 1,0 173 | 1,0 174 | 1,0 175 | 1,0 176 | 1,0 177 | 1,0 178 | 1,0 179 | 1,0 180 | 1,0 181 | 1,0 182 | 1,0 183 | 1,0 184 | 1,0 185 | 1,0 186 | 1,0 187 | 1,0 188 | 1,0 189 | -------------------------------------------------------------------------------- /examples/data/Y_mutag.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_mutag.mat -------------------------------------------------------------------------------- /examples/data/Y_nci1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_nci1.mat -------------------------------------------------------------------------------- /examples/data/Y_nci109.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_nci109.mat -------------------------------------------------------------------------------- /examples/data/Y_ptc.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/examples/data/Y_ptc.mat -------------------------------------------------------------------------------- /examples/data/cora/README: -------------------------------------------------------------------------------- 1 | This directory contains the a selection of the Cora dataset (www.research.whizbang.com/data). 2 | 3 | The Cora dataset consists of Machine Learning papers. These papers are classified into one of the following seven classes: 4 | Case_Based 5 | Genetic_Algorithms 6 | Neural_Networks 7 | Probabilistic_Methods 8 | Reinforcement_Learning 9 | Rule_Learning 10 | Theory 11 | 12 | The papers were selected in a way such that in the final corpus every paper cites or is cited by atleast one other paper. There are 2708 papers in the whole corpus. 13 | 14 | After stemming and removing stopwords we were left with a vocabulary of size 1433 unique words. All words with document frequency less than 10 were removed. 15 | 16 | 17 | THE DIRECTORY CONTAINS TWO FILES: 18 | 19 | The .content file contains descriptions of the papers in the following format: 20 | 21 | + 22 | 23 | The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper. Finally, the last entry in the line contains the class label of the paper. 24 | 25 | The .cites file contains the citation graph of the corpus. Each line describes a link in the following format: 26 | 27 | 28 | 29 | Each line contains two paper IDs. The first entry is the ID of the paper being cited and the second ID stands for the paper which contains the citation. The direction of the link is from right to left. If a line is represented by "paper1 paper2" then the link is "paper2->paper1". -------------------------------------------------------------------------------- /examples/gcnn_node_classification_example.py: -------------------------------------------------------------------------------- 1 | from keras.layers import Dense, Activation, Dropout 2 | from keras.models import Model, Sequential 3 | from keras.regularizers import l2 4 | from keras.optimizers import Adam 5 | import keras.backend as K 6 | import numpy as np 7 | 8 | from utils import * 9 | from keras_dgl.layers import GraphCNN 10 | 11 | 12 | # Prepare Data 13 | X, A, Y = load_data(dataset='cora') 14 | A = np.array(A.todense()) 15 | 16 | _, Y_val, _, train_idx, val_idx, test_idx, train_mask = get_splits(Y) 17 | train_idx = np.array(train_idx) 18 | val_idx = np.array(val_idx) 19 | test_idx = np.array(test_idx) 20 | labels = np.argmax(Y, axis=1) + 1 21 | 22 | # Normalize X 23 | X /= X.sum(1).reshape(-1, 1) 24 | X = np.array(X) 25 | 26 | Y_train = np.zeros(Y.shape) 27 | labels_train = np.zeros(labels.shape) 28 | Y_train[train_idx] = Y[train_idx] 29 | labels_train[train_idx] = labels[train_idx] 30 | 31 | Y_test = np.zeros(Y.shape) 32 | labels_test = np.zeros(labels.shape) 33 | Y_test[test_idx] = Y[test_idx] 34 | labels_test[test_idx] = labels[test_idx] 35 | 36 | # Build Graph Convolution filters 37 | SYM_NORM = True 38 | A_norm = preprocess_adj_numpy(A, SYM_NORM) 39 | num_filters = 2 40 | graph_conv_filters = np.concatenate([A_norm, np.matmul(A_norm, A_norm)], axis=0) 41 | graph_conv_filters = K.constant(graph_conv_filters) 42 | 43 | # Build Model 44 | model = Sequential() 45 | model.add(GraphCNN(16, num_filters, graph_conv_filters, input_shape=(X.shape[1],), activation='elu', kernel_regularizer=l2(5e-4))) 46 | model.add(Dropout(0.2)) 47 | model.add(GraphCNN(Y.shape[1], num_filters, graph_conv_filters, activation='elu', kernel_regularizer=l2(5e-4))) 48 | model.add(Activation('softmax')) 49 | model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.01), metrics=['acc']) 50 | 51 | nb_epochs = 500 52 | 53 | for epoch in range(nb_epochs): 54 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=1, shuffle=False, verbose=0) 55 | Y_pred = model.predict(X, batch_size=A.shape[0]) 56 | _, train_acc = evaluate_preds(Y_pred, [Y_train], [train_idx]) 57 | _, test_acc = evaluate_preds(Y_pred, [Y_test], [test_idx]) 58 | print("Epoch: {:04d}".format(epoch), "train_acc= {:.4f}".format(train_acc[0]), "test_acc= {:.4f}".format(test_acc[0])) 59 | 60 | # Sample Output 61 | # Epoch: 0495 train_acc= 0.9857 test_acc= 0.8310 62 | # Epoch: 0496 train_acc= 0.9857 test_acc= 0.8280 63 | # Epoch: 0497 train_acc= 0.9857 test_acc= 0.8260 64 | # Epoch: 0498 train_acc= 0.9857 test_acc= 0.8310 65 | # Epoch: 0499 train_acc= 0.9857 test_acc= 0.8350 66 | -------------------------------------------------------------------------------- /examples/gcnn_node_classification_with_edge_features_example.py: -------------------------------------------------------------------------------- 1 | from keras.layers import Dense, Activation, Dropout 2 | from keras.models import Model, Sequential 3 | from keras.regularizers import l2 4 | from keras.optimizers import Adam 5 | import keras.backend as K 6 | import numpy as np 7 | 8 | from utils import * 9 | from keras_dgl.layers import GraphCNN 10 | 11 | 12 | # Prepare Data 13 | X, A, Y = load_data(dataset='cora') 14 | A = np.array(A.todense()) 15 | 16 | _, Y_val, _, train_idx, val_idx, test_idx, train_mask = get_splits(Y) 17 | train_idx = np.array(train_idx) 18 | val_idx = np.array(val_idx) 19 | test_idx = np.array(test_idx) 20 | labels = np.argmax(Y, axis=1) + 1 21 | 22 | # Normalize X 23 | X /= X.sum(1).reshape(-1, 1) 24 | X = np.array(X) 25 | 26 | Y_train = np.zeros(Y.shape) 27 | labels_train = np.zeros(labels.shape) 28 | Y_train[train_idx] = Y[train_idx] 29 | labels_train[train_idx] = labels[train_idx] 30 | 31 | Y_test = np.zeros(Y.shape) 32 | labels_test = np.zeros(labels.shape) 33 | Y_test[test_idx] = Y[test_idx] 34 | labels_test[test_idx] = labels[test_idx] 35 | 36 | # Build Graph Convolution filters 37 | SYM_NORM = True 38 | A_norm = preprocess_adj_numpy(A, SYM_NORM) 39 | num_filters = 2 40 | graph_conv_filters = np.concatenate([A_norm, np.matmul(A_norm, A_norm)], axis=0) 41 | graph_conv_filters = K.constant(graph_conv_filters) 42 | 43 | # Build Model 44 | model = Sequential() 45 | model.add(GraphCNN(16, num_filters, graph_conv_filters, input_shape=(X.shape[1],), activation='elu', kernel_regularizer=l2(5e-4))) 46 | model.add(Dropout(0.2)) 47 | model.add(GraphCNN(Y.shape[1], num_filters, graph_conv_filters, activation='elu', kernel_regularizer=l2(5e-4))) 48 | model.add(Activation('softmax')) 49 | model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.01), metrics=['acc']) 50 | 51 | nb_epochs = 500 52 | 53 | for epoch in range(nb_epochs): 54 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=1, shuffle=False, verbose=0) 55 | Y_pred = model.predict(X, batch_size=A.shape[0]) 56 | _, train_acc = evaluate_preds(Y_pred, [Y_train], [train_idx]) 57 | _, test_acc = evaluate_preds(Y_pred, [Y_test], [test_idx]) 58 | print("Epoch: {:04d}".format(epoch), "train_acc= {:.4f}".format(train_acc[0]), "test_acc= {:.4f}".format(test_acc[0])) 59 | 60 | # Sample Output 61 | # Epoch: 0495 train_acc= 0.9857 test_acc= 0.8310 62 | # Epoch: 0496 train_acc= 0.9857 test_acc= 0.8280 63 | # Epoch: 0497 train_acc= 0.9857 test_acc= 0.8260 64 | # Epoch: 0498 train_acc= 0.9857 test_acc= 0.8310 65 | # Epoch: 0499 train_acc= 0.9857 test_acc= 0.8350 66 | -------------------------------------------------------------------------------- /examples/graph_attention_cnn_node_classification_example.py: -------------------------------------------------------------------------------- 1 | from keras.layers import Dense, Activation, Dropout 2 | from keras.models import Model, Sequential 3 | from keras.regularizers import l2 4 | from keras.optimizers import Adam 5 | import keras.backend as K 6 | import numpy as np 7 | 8 | from utils import * 9 | from keras_dgl.layers import GraphAttentionCNN 10 | 11 | 12 | # prepare data 13 | X, A, Y = load_data_attention(dataset='cora') 14 | A = np.array(A.todense()) 15 | 16 | _, Y_val, _, train_idx, val_idx, test_idx, train_mask = get_splits(Y) 17 | train_idx = np.array(train_idx) 18 | val_idx = np.array(val_idx) 19 | test_idx = np.array(test_idx) 20 | labels = np.argmax(Y, axis=1) + 1 21 | 22 | Y_train = np.zeros(Y.shape) 23 | labels_train = np.zeros(labels.shape) 24 | Y_train[train_idx] = Y[train_idx] 25 | labels_train[train_idx] = labels[train_idx] 26 | 27 | Y_test = np.zeros(Y.shape) 28 | labels_test = np.zeros(labels.shape) 29 | Y_test[test_idx] = Y[test_idx] 30 | labels_test[test_idx] = labels[test_idx] 31 | 32 | 33 | # build graph_conv_filters 34 | num_filters = 3 35 | graph_conv_filters = np.concatenate([np.eye(A.shape[0]), A, np.matmul(A, A)], axis=0) 36 | graph_conv_filters = K.constant(graph_conv_filters) 37 | 38 | 39 | # build model 40 | model = Sequential() 41 | model.add(Dropout(0.6, input_shape=(X.shape[1],))) 42 | model.add(GraphAttentionCNN(8, A, num_filters, graph_conv_filters, num_attention_heads=8, attention_combine='concat', attention_dropout=0.6, activation='elu', kernel_regularizer=l2(5e-4))) 43 | model.add(Dropout(0.6)) 44 | model.add(GraphAttentionCNN(Y.shape[1], A, num_filters, graph_conv_filters, num_attention_heads=1, attention_combine='average', attention_dropout=0.6, activation='elu', kernel_regularizer=l2(5e-4))) 45 | model.add(Activation('softmax')) 46 | model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.005), metrics=['accuracy']) 47 | 48 | NB_EPOCH = 1000 49 | 50 | for epoch in range(1, NB_EPOCH + 1): 51 | model.fit(X, Y_train, sample_weight=train_mask, batch_size=A.shape[0], epochs=1, shuffle=False, verbose=0) 52 | Y_pred = model.predict(X, batch_size=A.shape[0]) 53 | _, train_acc = evaluate_preds(Y_pred, [Y_train], [train_idx]) 54 | _, test_acc = evaluate_preds(Y_pred, [Y_test], [test_idx]) 55 | print("Epoch: {:04d}".format(epoch), "train_acc= {:.4f}".format(train_acc[0]), "test_acc= {:.4f}".format(test_acc[0])) 56 | 57 | # sample output 58 | # Epoch: 0990 train_acc= 0.9786 test_acc= 0.8470 59 | # Epoch: 0991 train_acc= 0.9786 test_acc= 0.8470 60 | # Epoch: 0992 train_acc= 0.9786 test_acc= 0.8460 61 | # Epoch: 0993 train_acc= 0.9786 test_acc= 0.8440 62 | # Epoch: 0994 train_acc= 0.9786 test_acc= 0.8430 63 | # Epoch: 0995 train_acc= 0.9786 test_acc= 0.8440 64 | # Epoch: 0996 train_acc= 0.9786 test_acc= 0.8410 65 | # Epoch: 0997 train_acc= 0.9786 test_acc= 0.8410 66 | # Epoch: 0998 train_acc= 0.9786 test_acc= 0.8410 67 | # Epoch: 0999 train_acc= 0.9786 test_acc= 0.8400 68 | # Epoch: 1000 train_acc= 0.9786 test_acc= 0.8420 69 | -------------------------------------------------------------------------------- /examples/multi_gcnn_graph_classification_example.py: -------------------------------------------------------------------------------- 1 | from keras.models import Input, Model, Sequential 2 | from keras.layers import Dense, Activation, Dropout, Lambda 3 | import keras.backend as K 4 | import numpy as np 5 | import pandas as pd 6 | from sklearn.utils import shuffle 7 | 8 | from utils import * 9 | from keras_dgl.layers import MultiGraphCNN 10 | 11 | # prepare data 12 | A = pd.read_csv('data/A_mutag.csv', header=None) 13 | A = np.array(A) 14 | num_graph_nodes = A.shape[1] 15 | num_graphs = int(A.shape[0]/A.shape[1]) 16 | 17 | A = np.split(A, num_graphs, axis=0) 18 | A = np.array(A) 19 | 20 | X = pd.read_csv('data/X_mutag.csv', header=None) 21 | X = np.array(X) 22 | X = np.split(X, num_graphs, axis=0) 23 | X = np.array(X) 24 | 25 | Y = pd.read_csv('data/Y_mutag.csv', header=None) 26 | Y = np.array(Y) 27 | 28 | A, X, Y = shuffle(A, X, Y) 29 | 30 | # build graph_conv_filters 31 | SYM_NORM = True 32 | num_filters = 2 33 | graph_conv_filters = preprocess_adj_tensor_with_identity(A, SYM_NORM) 34 | 35 | # build model 36 | X_input = Input(shape=(X.shape[1], X.shape[2])) 37 | graph_conv_filters_input = Input(shape=(graph_conv_filters.shape[1], graph_conv_filters.shape[2])) 38 | 39 | output = MultiGraphCNN(100, num_filters, activation='elu')([X_input, graph_conv_filters_input]) 40 | output = Dropout(0.2)(output) 41 | output = MultiGraphCNN(100, num_filters, activation='elu')([output, graph_conv_filters_input]) 42 | output = Dropout(0.2)(output) 43 | output = Lambda(lambda x: K.mean(x, axis=1))(output) # adding a node invariant layer to make sure output does not depends upon the node order in a graph. 44 | output = Dense(Y.shape[1])(output) 45 | output = Activation('softmax')(output) 46 | 47 | nb_epochs = 200 48 | batch_size = 169 49 | 50 | model = Model(inputs=[X_input, graph_conv_filters_input], outputs=output) 51 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 52 | model.fit([X, graph_conv_filters], Y, batch_size=batch_size, validation_split=0.1, epochs=nb_epochs, shuffle=True, verbose=1) 53 | 54 | # sample output 55 | # 169/169 [==============================] - 0s 66us/step - loss: 0.3526 - acc: 0.8639 - val_loss: 0.4241 - val_acc: 0.8947 56 | # Epoch 198/200 57 | # 169/169 [==============================] - 0s 67us/step - loss: 0.3558 - acc: 0.8580 - val_loss: 0.4261 - val_acc: 0.8947 58 | # Epoch 199/200 59 | # 169/169 [==============================] - 0s 62us/step - loss: 0.3555 - acc: 0.8462 - val_loss: 0.4276 - val_acc: 0.8947 60 | # Epoch 200/200 61 | # 169/169 [==============================] - 0s 64us/step - loss: 0.3546 - acc: 0.8521 - val_loss: 0.4273 - val_acc: 0.8947 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/multi_gcnn_graph_classification_with_edge_features_example.py: -------------------------------------------------------------------------------- 1 | from keras.models import Input, Model, Sequential 2 | from keras.layers import Dense, Activation, Dropout, Lambda 3 | import keras.backend as K 4 | import numpy as np 5 | import pandas as pd 6 | from sklearn.utils import shuffle 7 | 8 | from utils import * 9 | from keras_dgl.layers import MultiGraphCNN 10 | 11 | # prepare data 12 | A = pd.read_csv('data/A_edge_matrices_mutag.csv', header=None) 13 | A = np.array(A) 14 | num_graph_nodes = A.shape[1] 15 | num_graphs = 188 # hardcoded for mutag dataset 16 | 17 | A = np.split(A, num_graphs, axis=0) 18 | A = np.array(A) 19 | num_edge_features = int(A.shape[1]/A.shape[2]) 20 | 21 | X = pd.read_csv('data/X_mutag.csv', header=None) 22 | X = np.array(X) 23 | X = np.split(X, num_graphs, axis=0) 24 | X = np.array(X) 25 | 26 | Y = pd.read_csv('data/Y_mutag.csv', header=None) 27 | Y = np.array(Y) 28 | 29 | A, X, Y = shuffle(A, X, Y) 30 | 31 | # build graph_conv_filters 32 | SYM_NORM = True 33 | num_filters = num_edge_features 34 | graph_conv_filters = preprocess_edge_adj_tensor(A, SYM_NORM) 35 | 36 | # build model 37 | X_input = Input(shape=(X.shape[1], X.shape[2])) 38 | graph_conv_filters_input = Input(shape=(graph_conv_filters.shape[1], graph_conv_filters.shape[2])) 39 | 40 | output = MultiGraphCNN(100, num_filters, activation='elu')([X_input, graph_conv_filters_input]) 41 | output = Dropout(0.2)(output) 42 | output = MultiGraphCNN(100, num_filters, activation='elu')([output, graph_conv_filters_input]) 43 | output = Dropout(0.2)(output) 44 | output = Lambda(lambda x: K.mean(x, axis=1))(output) # adding a node invariant layer to make sure output does not depends upon the node order in a graph. 45 | output = Dense(Y.shape[1])(output) 46 | output = Activation('softmax')(output) 47 | 48 | nb_epochs = 200 49 | batch_size = 169 50 | 51 | model = Model(inputs=[X_input, graph_conv_filters_shape], outputs=output) 52 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 53 | model.fit([X, graph_conv_filters], Y, batch_size=batch_size, validation_split=0.1, epochs=nb_epochs, shuffle=True, verbose=1) 54 | 55 | # sample output 56 | # 169/169 [==============================] - 0s 90us/step - loss: 0.3578 - acc: 0.8462 - val_loss: 0.2174 - val_acc: 0.8947 57 | # Epoch 496/500 58 | # 169/169 [==============================] - 0s 100us/step - loss: 0.3748 - acc: 0.8521 - val_loss: 0.2179 - val_acc: 0.8947 59 | # Epoch 497/500 60 | # 169/169 [==============================] - 0s 113us/step - loss: 0.3656 - acc: 0.8521 - val_loss: 0.2186 - val_acc: 0.8947 61 | # Epoch 498/500 62 | # 169/169 [==============================] - 0s 102us/step - loss: 0.3592 - acc: 0.8462 - val_loss: 0.2178 - val_acc: 0.8947 63 | # Epoch 499/500 64 | # 169/169 [==============================] - 0s 102us/step - loss: 0.3746 - acc: 0.8521 - val_loss: 0.2160 - val_acc: 0.8947 65 | # Epoch 500/500 66 | # 169/169 [==============================] - 0s 99us/step - loss: 0.3710 - acc: 0.8580 - val_loss: 0.2152 - val_acc: 0.8947 67 | 68 | -------------------------------------------------------------------------------- /examples/multi_graph_attention_cnn_graph_classification_example.py: -------------------------------------------------------------------------------- 1 | from keras.models import Input, Model, Sequential 2 | from keras.layers import Dense, Activation, Dropout, Lambda 3 | from keras.regularizers import l2 4 | import keras.backend as K 5 | import numpy as np 6 | import pandas as pd 7 | from sklearn.utils import shuffle 8 | 9 | from utils import * 10 | from keras_dgl.layers import MultiGraphAttentionCNN 11 | 12 | # prepare data 13 | A = pd.read_csv('data/A_mutag.csv', header=None) 14 | A = np.array(A) 15 | num_graph_nodes = A.shape[1] 16 | num_graphs = int(A.shape[0] / A.shape[1]) 17 | 18 | A = np.split(A, num_graphs, axis=0) 19 | A = np.array(A) 20 | 21 | X = pd.read_csv('data/X_mutag.csv', header=None) 22 | X = np.array(X) 23 | X = np.split(X, num_graphs, axis=0) 24 | X = np.array(X) 25 | 26 | 27 | Y = pd.read_csv('data/Y_mutag.csv', header=None) 28 | Y = np.array(Y) 29 | 30 | A, X, Y = shuffle(A, X, Y) 31 | 32 | # build graph_conv_filters 33 | SYM_NORM = True 34 | num_filters = 2 35 | graph_conv_filters = preprocess_adj_tensor_with_identity(A, SYM_NORM) 36 | 37 | # set daigonal values to 1 in adjacency matrices 38 | A_eye_tensor = [] 39 | for _ in range(num_graphs): 40 | Identity_matrix = np.eye(num_graph_nodes) 41 | A_eye_tensor.append(Identity_matrix) 42 | 43 | A_eye_tensor = np.array(A_eye_tensor) 44 | A = np.add(A, A_eye_tensor) 45 | 46 | # build model 47 | X_input = Input(shape=(X.shape[1], X.shape[2])) 48 | A_input = Input(shape=(A.shape[1], A.shape[2])) 49 | graph_conv_filters_input = Input(shape=(graph_conv_filters.shape[1], graph_conv_filters.shape[2])) 50 | 51 | output = MultiGraphAttentionCNN(100, num_filters=num_filters, num_attention_heads=2, attention_combine='concat', attention_dropout=0.5, activation='elu', kernel_regularizer=l2(5e-4))([X_input, A_input, graph_conv_filters_input]) 52 | output = Dropout(0.2)(output) 53 | output = MultiGraphAttentionCNN(100, num_filters=num_filters, num_attention_heads=1, attention_combine='average', attention_dropout=0.5, activation='elu', kernel_regularizer=l2(5e-4))([output, A_input, graph_conv_filters_input]) 54 | output = Dropout(0.2)(output) 55 | output = Lambda(lambda x: K.mean(x, axis=1))(output) # adding a node invariant layer to make sure output does not depends upon the node order in a graph. 56 | output = Dense(Y.shape[1], activation='elu')(output) 57 | output = Activation('softmax')(output) 58 | 59 | nb_epochs = 500 60 | batch_size = 169 61 | 62 | model = Model(inputs=[X_input, A_input, graph_conv_filters_input], outputs=output) 63 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) 64 | model.fit([X, A, graph_conv_filters], Y, batch_size=batch_size, validation_split=0.1, epochs=nb_epochs, shuffle=True, verbose=1) 65 | 66 | # sample output 67 | # 169/169 [==============================] - 0s 138us/step - loss: 0.5014 - acc: 0.7633 - val_loss: 0.3162 - val_acc: 0.8436 68 | # Epoch 496/500 69 | # 169/169 [==============================] - 0s 127us/step - loss: 0.4770 - acc: 0.7633 - val_loss: 0.3187 - val_acc: 0.8436 70 | # Epoch 497/500 71 | # 169/169 [==============================] - 0s 131us/step - loss: 0.4781 - acc: 0.7574 - val_loss: 0.3196 - val_acc: 0.8436 72 | # Epoch 498/500 73 | # 169/169 [==============================] - 0s 120us/step - loss: 0.4925 - acc: 0.7574 - val_loss: 0.3197 - val_acc: 0.8436 74 | # Epoch 499/500 75 | # 169/169 [==============================] - 0s 137us/step - loss: 0.4911 - acc: 0.7692 - val_loss: 0.3161 - val_acc: 0.8436 76 | # Epoch 500/500 77 | # 169/169 [==============================] - 0s 127us/step - loss: 0.5004 - acc: 0.7633 - val_loss: 0.3130 - val_acc: 0.8436 78 | -------------------------------------------------------------------------------- /examples/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import scipy.sparse as sp 4 | import numpy as np 5 | from scipy.sparse.linalg.eigen.arpack import eigsh, ArpackNoConvergence 6 | 7 | 8 | def encode_onehot(labels): 9 | classes = set(labels) 10 | classes_dict = {c: np.identity(len(classes))[i, :] for i, c in enumerate(classes)} 11 | labels_onehot = np.array(list(map(classes_dict.get, labels)), dtype=np.int32) 12 | return labels_onehot 13 | 14 | 15 | def load_data(path="data/cora/", dataset="cora"): 16 | """Load citation network dataset (cora only for now)""" 17 | print('Loading {} dataset...'.format(dataset)) 18 | 19 | idx_features_labels = np.genfromtxt("{}{}.content".format(path, dataset), dtype=np.dtype(str)) 20 | features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32) 21 | labels = encode_onehot(idx_features_labels[:, -1]) 22 | 23 | # build graph 24 | idx = np.array(idx_features_labels[:, 0], dtype=np.int32) 25 | idx_map = {j: i for i, j in enumerate(idx)} 26 | edges_unordered = np.genfromtxt("{}{}.cites".format(path, dataset), dtype=np.int32) 27 | edges = np.array(list(map(idx_map.get, edges_unordered.flatten())), 28 | dtype=np.int32).reshape(edges_unordered.shape) 29 | adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])), 30 | shape=(labels.shape[0], labels.shape[0]), dtype=np.float32) 31 | 32 | # build symmetric adjacency matrix 33 | adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) 34 | 35 | # features = normalize_features(features) 36 | # adj = normalize_adj(adj + sp.eye(adj.shape[0])) 37 | 38 | print('Dataset has {} nodes, {} edges, {} features.'.format(adj.shape[0], edges.shape[0], features.shape[1])) 39 | 40 | return features.todense(), adj, labels 41 | 42 | 43 | def load_data_attention(path="data/cora/", dataset="cora"): 44 | """Load citation network dataset (cora only for now)""" 45 | print('Loading {} dataset...'.format(dataset)) 46 | 47 | idx_features_labels = np.genfromtxt("{}{}.content".format(path, dataset), dtype=np.dtype(str)) 48 | features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32) 49 | labels = encode_onehot(idx_features_labels[:, -1]) 50 | 51 | # build graph 52 | idx = np.array(idx_features_labels[:, 0], dtype=np.int32) 53 | idx_map = {j: i for i, j in enumerate(idx)} 54 | edges_unordered = np.genfromtxt("{}{}.cites".format(path, dataset), dtype=np.int32) 55 | edges = np.array(list(map(idx_map.get, edges_unordered.flatten())), 56 | dtype=np.int32).reshape(edges_unordered.shape) 57 | adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])), 58 | shape=(labels.shape[0], labels.shape[0]), dtype=np.float32) 59 | 60 | # build symmetric adjacency matrix 61 | adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) 62 | 63 | features = normalize_features(features) 64 | adj = normalize_adj(adj + sp.eye(adj.shape[0])) 65 | 66 | print('Dataset has {} nodes, {} edges, {} features.'.format(adj.shape[0], edges.shape[0], features.shape[1])) 67 | 68 | return features.todense(), adj, labels 69 | 70 | 71 | def normalize_features(mx): 72 | """Row-normalize sparse matrix""" 73 | rowsum = np.array(mx.sum(1)) 74 | r_inv = np.power(rowsum, -1).flatten() 75 | r_inv[np.isinf(r_inv)] = 0. 76 | r_mat_inv = sp.diags(r_inv) 77 | mx = r_mat_inv.dot(mx) 78 | return mx 79 | 80 | 81 | def normalize_adj(adj, symmetric=True): 82 | if symmetric: 83 | d = sp.diags(np.power(np.array(adj.sum(1)), -0.5).flatten(), 0) 84 | a_norm = adj.dot(d).transpose().dot(d).tocsr() 85 | else: 86 | d = sp.diags(np.power(np.array(adj.sum(1)), -1).flatten(), 0) 87 | a_norm = d.dot(adj).tocsr() 88 | return a_norm 89 | 90 | 91 | def normalize_adj_numpy(adj, symmetric=True): 92 | if symmetric: 93 | d = np.diag(np.power(np.array(adj.sum(1)), -0.5).flatten(), 0) 94 | a_norm = adj.dot(d).transpose().dot(d) 95 | else: 96 | d = np.diag(np.power(np.array(adj.sum(1)), -1).flatten(), 0) 97 | a_norm = d.dot(adj) 98 | return a_norm 99 | 100 | 101 | def preprocess_adj(adj, symmetric=True): 102 | adj = adj + sp.eye(adj.shape[0]) 103 | adj = normalize_adj(adj, symmetric) 104 | return adj 105 | 106 | 107 | def preprocess_adj_numpy(adj, symmetric=True): 108 | adj = adj + np.eye(adj.shape[0]) 109 | adj = normalize_adj_numpy(adj, symmetric) 110 | return adj 111 | 112 | 113 | def preprocess_adj_tensor(adj_tensor, symmetric=True): 114 | adj_out_tensor = [] 115 | for i in range(adj_tensor.shape[0]): 116 | adj = adj_tensor[i] 117 | adj = adj + np.eye(adj.shape[0]) 118 | adj = normalize_adj_numpy(adj, symmetric) 119 | adj_out_tensor.append(adj) 120 | adj_out_tensor = np.array(adj_out_tensor) 121 | return adj_out_tensor 122 | 123 | 124 | def preprocess_adj_tensor_with_identity(adj_tensor, symmetric=True): 125 | adj_out_tensor = [] 126 | for i in range(adj_tensor.shape[0]): 127 | adj = adj_tensor[i] 128 | adj = adj + np.eye(adj.shape[0]) 129 | adj = normalize_adj_numpy(adj, symmetric) 130 | adj = np.concatenate([np.eye(adj.shape[0]), adj], axis=0) 131 | adj_out_tensor.append(adj) 132 | adj_out_tensor = np.array(adj_out_tensor) 133 | return adj_out_tensor 134 | 135 | 136 | def preprocess_adj_tensor_with_identity_concat(adj_tensor, symmetric=True): 137 | adj_out_tensor = [] 138 | for i in range(adj_tensor.shape[0]): 139 | adj = adj_tensor[i] 140 | adj = adj + np.eye(adj.shape[0]) 141 | adj = normalize_adj_numpy(adj, symmetric) 142 | adj = np.concatenate([np.eye(adj.shape[0]), adj], axis=0) 143 | adj_out_tensor.append(adj) 144 | adj_out_tensor = np.concatenate(adj_out_tensor, axis=0) 145 | return adj_out_tensor 146 | 147 | def preprocess_adj_tensor_concat(adj_tensor, symmetric=True): 148 | adj_out_tensor = [] 149 | for i in range(adj_tensor.shape[0]): 150 | adj = adj_tensor[i] 151 | adj = adj + np.eye(adj.shape[0]) 152 | adj = normalize_adj_numpy(adj, symmetric) 153 | adj_out_tensor.append(adj) 154 | adj_out_tensor = np.concatenate(adj_out_tensor, axis=0) 155 | return adj_out_tensor 156 | 157 | def preprocess_edge_adj_tensor(edge_adj_tensor, symmetric=True): 158 | edge_adj_out_tensor = [] 159 | num_edge_features = int(edge_adj_tensor.shape[1]/edge_adj_tensor.shape[2]) 160 | 161 | for i in range(edge_adj_tensor.shape[0]): 162 | edge_adj = edge_adj_tensor[i] 163 | edge_adj = np.split(edge_adj, num_edge_features, axis=0) 164 | edge_adj = np.array(edge_adj) 165 | edge_adj = preprocess_adj_tensor_concat(edge_adj, symmetric) 166 | edge_adj_out_tensor.append(edge_adj) 167 | 168 | edge_adj_out_tensor = np.array(edge_adj_out_tensor) 169 | return edge_adj_out_tensor 170 | 171 | 172 | def sample_mask(idx, l): 173 | mask = np.zeros(l) 174 | mask[idx] = 1 175 | return np.array(mask, dtype=np.bool) 176 | 177 | 178 | def get_splits(y): 179 | idx_train = range(140) 180 | idx_val = range(200, 500) 181 | idx_test = range(500, 1500) 182 | y_train = np.zeros(y.shape, dtype=np.int32) 183 | y_val = np.zeros(y.shape, dtype=np.int32) 184 | y_test = np.zeros(y.shape, dtype=np.int32) 185 | y_train[idx_train] = y[idx_train] 186 | y_val[idx_val] = y[idx_val] 187 | y_test[idx_test] = y[idx_test] 188 | train_mask = sample_mask(idx_train, y.shape[0]) 189 | return y_train, y_val, y_test, idx_train, idx_val, idx_test, train_mask 190 | 191 | 192 | def get_splits_v2(y): 193 | idx_train = range(1708) 194 | idx_val = range(1708, 1708 + 500) 195 | idx_test = range(1708 + 500, 2708) 196 | y_train = np.zeros(y.shape, dtype=np.int32) 197 | y_val = np.zeros(y.shape, dtype=np.int32) 198 | y_test = np.zeros(y.shape, dtype=np.int32) 199 | y_train[idx_train] = y[idx_train] 200 | y_val[idx_val] = y[idx_val] 201 | y_test[idx_test] = y[idx_test] 202 | train_mask = sample_mask(idx_train, y.shape[0]) 203 | return y_train, y_val, y_test, idx_train, idx_val, idx_test, train_mask 204 | 205 | 206 | def categorical_crossentropy(preds, labels): 207 | return np.mean(-np.log(np.extract(labels, preds))) 208 | 209 | 210 | def accuracy(preds, labels): 211 | return np.mean(np.equal(np.argmax(labels, 1), np.argmax(preds, 1))) 212 | 213 | 214 | def evaluate_preds(preds, labels, indices): 215 | split_loss = list() 216 | split_acc = list() 217 | 218 | for y_split, idx_split in zip(labels, indices): 219 | split_loss.append(categorical_crossentropy(preds[idx_split], y_split[idx_split])) 220 | split_acc.append(accuracy(preds[idx_split], y_split[idx_split])) 221 | 222 | return split_loss, split_acc 223 | 224 | 225 | def normalized_laplacian(adj, symmetric=True): 226 | adj_normalized = normalize_adj(adj, symmetric) 227 | laplacian = sp.eye(adj.shape[0]) - adj_normalized 228 | return laplacian 229 | 230 | 231 | def rescale_laplacian(laplacian): 232 | try: 233 | print('Calculating largest eigenvalue of normalized graph Laplacian...') 234 | largest_eigval = eigsh(laplacian, 1, which='LM', return_eigenvectors=False)[0] 235 | except ArpackNoConvergence: 236 | print('Eigenvalue calculation did not converge! Using largest_eigval=2 instead.') 237 | largest_eigval = 2 238 | 239 | scaled_laplacian = (2. / largest_eigval) * laplacian - sp.eye(laplacian.shape[0]) 240 | return scaled_laplacian 241 | 242 | 243 | def chebyshev_polynomial(X, k): 244 | """Calculate Chebyshev polynomials up to order k. Return a list of sparse matrices.""" 245 | print("Calculating Chebyshev polynomials up to order {}...".format(k)) 246 | 247 | T_k = list() 248 | T_k.append(sp.eye(X.shape[0]).tocsr()) 249 | T_k.append(X) 250 | 251 | def chebyshev_recurrence(T_k_minus_one, T_k_minus_two, X): 252 | X_ = sp.csr_matrix(X, copy=True) 253 | return 2 * X_.dot(T_k_minus_one) - T_k_minus_two 254 | 255 | for i in range(2, k + 1): 256 | T_k.append(chebyshev_recurrence(T_k[-1], T_k[-2], X)) 257 | 258 | return T_k 259 | 260 | 261 | def sparse_to_tuple(sparse_mx): 262 | if not sp.isspmatrix_coo(sparse_mx): 263 | sparse_mx = sparse_mx.tocoo() 264 | coords = np.vstack((sparse_mx.row, sparse_mx.col)).transpose() 265 | values = sparse_mx.data 266 | shape = sparse_mx.shape 267 | return coords, values, shape 268 | -------------------------------------------------------------------------------- /keras_dgl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/keras_dgl/__init__.py -------------------------------------------------------------------------------- /keras_dgl/layers/__init__.py: -------------------------------------------------------------------------------- 1 | from .graph_cnn_layer import GraphCNN 2 | from .multi_graph_cnn_layer import MultiGraphCNN 3 | from .graph_attention_cnn_layer import GraphAttentionCNN 4 | from .multi_graph_attention_cnn_layer import MultiGraphAttentionCNN 5 | from .graph_convolutional_recurrent_layer import GraphConvLSTM -------------------------------------------------------------------------------- /keras_dgl/layers/graph_attention_cnn_layer.py: -------------------------------------------------------------------------------- 1 | from keras import activations, initializers, constraints 2 | from keras import regularizers 3 | import keras.backend as K 4 | from keras.engine.topology import Layer 5 | from keras.layers import Dropout, LeakyReLU, ELU 6 | import tensorflow as tf 7 | from .graph_ops import graph_conv_op 8 | 9 | 10 | class GraphAttentionCNN(Layer): 11 | 12 | def __init__(self, 13 | output_dim, 14 | adjacency_matrix, 15 | num_filters=None, 16 | graph_conv_filters=None, 17 | num_attention_heads=1, 18 | attention_combine='concat', 19 | attention_dropout=0.5, 20 | activation=None, 21 | use_bias=False, 22 | kernel_initializer='glorot_uniform', 23 | bias_initializer='zeros', 24 | kernel_regularizer=None, 25 | bias_regularizer=None, 26 | activity_regularizer=None, 27 | kernel_constraint=None, 28 | bias_constraint=None, 29 | **kwargs): 30 | 31 | if attention_combine not in {'concat', 'average'}: 32 | raise ValueError('Possbile reduction methods: concat, average') 33 | 34 | super(GraphAttentionCNN, self).__init__(**kwargs) 35 | 36 | self.output_dim = output_dim 37 | self.adjacency_matrix = K.constant(adjacency_matrix) 38 | 39 | self.num_filters = num_filters 40 | if self.num_filters is not None: 41 | if self.num_filters != int(graph_conv_filters.get_shape().as_list()[-2]/graph_conv_filters.get_shape().as_list()[-1]): 42 | raise ValueError('num_filters does not match with graph_conv_filters dimensions.') 43 | self.graph_conv_filters = graph_conv_filters 44 | 45 | self.num_attention_heads = num_attention_heads 46 | self.attention_combine = attention_combine 47 | self.attention_dropout = attention_dropout 48 | 49 | self.activation = activations.get(activation) 50 | self.use_bias = use_bias 51 | self.kernel_initializer = initializers.get(kernel_initializer) 52 | self.kernel_initializer.__name__ = kernel_initializer 53 | self.bias_initializer = initializers.get(bias_initializer) 54 | self.kernel_regularizer = regularizers.get(kernel_regularizer) 55 | self.bias_regularizer = regularizers.get(bias_regularizer) 56 | self.activity_regularizer = regularizers.get(activity_regularizer) 57 | self.kernel_constraint = constraints.get(kernel_constraint) 58 | self.bias_constraint = constraints.get(bias_constraint) 59 | self.supports_masking = False 60 | 61 | self.input_dim = None 62 | self.kernels = [] 63 | self.kernels_biases = [] 64 | self.attention_kernels = [] 65 | self.attention_kernels_biases = [] 66 | 67 | def build(self, input_shape): 68 | 69 | self.input_dim = input_shape[-1] 70 | 71 | if self.num_filters is not None: 72 | kernel_shape = (self.num_filters * self.input_dim, self.output_dim) 73 | else: 74 | kernel_shape = (self.input_dim, self.output_dim) 75 | 76 | attention_kernel_shape = (2 * self.output_dim, 1) 77 | 78 | for _ in range(self.num_attention_heads): 79 | 80 | kernel = self.add_weight(shape=kernel_shape, 81 | initializer=self.kernel_initializer, 82 | name='kernel', 83 | regularizer=self.kernel_regularizer, 84 | constraint=self.kernel_constraint) 85 | self.kernels.append(kernel) 86 | 87 | if self.use_bias: 88 | bias = self.add_weight(shape=(self.output_dim,), 89 | initializer=self.bias_initializer, 90 | name='bias', 91 | regularizer=self.bias_regularizer, 92 | constraint=self.bias_constraint) 93 | else: 94 | bias = None 95 | self.kernels_biases.append(bias) 96 | 97 | attention_kernel = self.add_weight(shape=attention_kernel_shape, 98 | initializer=self.kernel_initializer, 99 | name='attention_kernel', 100 | regularizer=self.kernel_regularizer, 101 | constraint=self.kernel_constraint) 102 | 103 | self.attention_kernels.append(attention_kernel) 104 | if self.use_bias: 105 | bias = self.add_weight(shape=(1,), 106 | initializer=self.bias_initializer, 107 | name='attention_bias', 108 | regularizer=self.bias_regularizer, 109 | constraint=self.bias_constraint) 110 | else: 111 | bias = None 112 | self.attention_kernels_biases.append(bias) 113 | 114 | self.built = True 115 | 116 | def call(self, input): 117 | 118 | outputs = [] 119 | 120 | for i in range(self.num_attention_heads): 121 | 122 | if self.num_filters is not None: 123 | conv_out = graph_conv_op(input, self.num_filters, self.graph_conv_filters, self.kernels[i]) 124 | else: 125 | conv_out = K.dot(input, self.kernels[i]) 126 | 127 | if self.use_bias: 128 | conv_out = K.bias_add(conv_out, self.kernels_biases[i]) 129 | 130 | atten_conv_out_self = K.dot(conv_out, self.attention_kernels[i][:self.output_dim]) 131 | atten_conv_out_neigh = K.dot(conv_out, self.attention_kernels[i][self.output_dim:]) 132 | 133 | if self.use_bias: 134 | atten_conv_out_self = K.bias_add(atten_conv_out_self, self.attention_kernels_biases[i]) 135 | 136 | atten_coeff_matrix = atten_conv_out_self + K.transpose(atten_conv_out_neigh) 137 | atten_coeff_matrix = ELU(alpha=1.0)(atten_coeff_matrix) # can be replaced by LeakyReLU(alpha=0.2) as set in the paper 138 | 139 | mask = K.exp(self.adjacency_matrix * -10e9) * -10e9 140 | atten_coeff_matrix = atten_coeff_matrix + mask 141 | 142 | atten_coeff_matrix = K.softmax(atten_coeff_matrix) 143 | atten_coeff_matrix = Dropout(self.attention_dropout)(atten_coeff_matrix) 144 | 145 | node_feature_matrix = K.dot(atten_coeff_matrix, conv_out) 146 | 147 | if self.attention_combine == 'concat' and self.activation is not None: 148 | node_feature_matrix = self.activation(node_feature_matrix) 149 | 150 | outputs.append(node_feature_matrix) 151 | 152 | if self.attention_combine == 'concat': 153 | output = K.concatenate(outputs) 154 | else: 155 | output = K.mean(K.stack(outputs), axis=0) 156 | if self.activation is not None: 157 | output = self.activation(output) 158 | 159 | return output 160 | 161 | def compute_output_shape(self, input_shape): 162 | if self.attention_combine == 'concat': 163 | actutal_output_dim = self.output_dim * self.num_attention_heads 164 | else: 165 | actutal_output_dim = self.output_dim 166 | output_shape = (input_shape[0], actutal_output_dim) 167 | return output_shape 168 | 169 | def get_config(self): 170 | config = { 171 | 'output_dim': self.output_dim, 172 | 'adjacency_matrix': self.adjacency_matrix, 173 | 'num_filters': self.num_filters, 174 | 'graph_conv_filters': self.graph_conv_filters, 175 | 'num_attention_heads': self.num_attention_heads, 176 | 'attention_combine': self.attention_combine, 177 | 'attention_dropout': self.attention_dropout, 178 | 'activation': activations.serialize(self.activation), 179 | 'use_bias': self.use_bias, 180 | 'kernel_initializer': initializers.serialize(self.kernel_initializer), 181 | 'bias_initializer': initializers.serialize(self.bias_initializer), 182 | 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer), 183 | 'bias_regularizer': regularizers.serialize(self.bias_regularizer), 184 | 'activity_regularizer': regularizers.serialize(self.activity_regularizer), 185 | 'kernel_constraint': constraints.serialize(self.kernel_constraint), 186 | 'bias_constraint': constraints.serialize(self.bias_constraint) 187 | } 188 | base_config = super(GraphAttentionCNN, self).get_config() 189 | return dict(list(base_config.items()) + list(config.items())) 190 | -------------------------------------------------------------------------------- /keras_dgl/layers/graph_cnn_layer.py: -------------------------------------------------------------------------------- 1 | from keras import activations, initializers, constraints 2 | from keras import regularizers 3 | import keras.backend as K 4 | from keras.engine.topology import Layer 5 | import tensorflow as tf 6 | from .graph_ops import graph_conv_op 7 | 8 | 9 | class GraphCNN(Layer): 10 | 11 | def __init__(self, 12 | output_dim, 13 | num_filters, 14 | graph_conv_filters, 15 | activation=None, 16 | use_bias=True, 17 | kernel_initializer='glorot_uniform', 18 | bias_initializer='zeros', 19 | kernel_regularizer=None, 20 | bias_regularizer=None, 21 | activity_regularizer=None, 22 | kernel_constraint=None, 23 | bias_constraint=None, 24 | **kwargs): 25 | super(GraphCNN, self).__init__(**kwargs) 26 | 27 | self.output_dim = output_dim 28 | self.num_filters = num_filters 29 | if num_filters != int(graph_conv_filters.get_shape().as_list()[-2]/graph_conv_filters.get_shape().as_list()[-1]): 30 | raise ValueError('num_filters does not match with graph_conv_filters dimensions.') 31 | self.graph_conv_filters = graph_conv_filters 32 | 33 | self.activation = activations.get(activation) 34 | self.use_bias = use_bias 35 | self.kernel_initializer = initializers.get(kernel_initializer) 36 | self.kernel_initializer.__name__ = kernel_initializer 37 | self.bias_initializer = initializers.get(bias_initializer) 38 | self.kernel_regularizer = regularizers.get(kernel_regularizer) 39 | self.bias_regularizer = regularizers.get(bias_regularizer) 40 | self.activity_regularizer = regularizers.get(activity_regularizer) 41 | self.kernel_constraint = constraints.get(kernel_constraint) 42 | self.bias_constraint = constraints.get(bias_constraint) 43 | 44 | def build(self, input_shape): 45 | 46 | self.input_dim = input_shape[-1] 47 | kernel_shape = (self.num_filters * self.input_dim, self.output_dim) 48 | 49 | self.kernel = self.add_weight(shape=kernel_shape, 50 | initializer=self.kernel_initializer, 51 | name='kernel', 52 | regularizer=self.kernel_regularizer, 53 | constraint=self.kernel_constraint) 54 | if self.use_bias: 55 | self.bias = self.add_weight(shape=(self.output_dim,), 56 | initializer=self.bias_initializer, 57 | name='bias', 58 | regularizer=self.bias_regularizer, 59 | constraint=self.bias_constraint) 60 | else: 61 | self.bias = None 62 | 63 | self.built = True 64 | 65 | def call(self, input): 66 | 67 | output = graph_conv_op(input, self.num_filters, self.graph_conv_filters, self.kernel) 68 | if self.use_bias: 69 | output = K.bias_add(output, self.bias) 70 | if self.activation is not None: 71 | output = self.activation(output) 72 | return output 73 | 74 | def compute_output_shape(self, input_shape): 75 | output_shape = (input_shape[0], self.output_dim) 76 | return output_shape 77 | 78 | def get_config(self): 79 | config = { 80 | 'output_dim': self.output_dim, 81 | 'num_filters': self.num_filters, 82 | 'graph_conv_filters': self.graph_conv_filters, 83 | 'activation': activations.serialize(self.activation), 84 | 'use_bias': self.use_bias, 85 | 'kernel_initializer': initializers.serialize(self.kernel_initializer), 86 | 'bias_initializer': initializers.serialize(self.bias_initializer), 87 | 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer), 88 | 'bias_regularizer': regularizers.serialize(self.bias_regularizer), 89 | 'activity_regularizer': regularizers.serialize(self.activity_regularizer), 90 | 'kernel_constraint': constraints.serialize(self.kernel_constraint), 91 | 'bias_constraint': constraints.serialize(self.bias_constraint) 92 | } 93 | base_config = super(GraphCNN, self).get_config() 94 | return dict(list(base_config.items()) + list(config.items())) 95 | -------------------------------------------------------------------------------- /keras_dgl/layers/graph_ops.py: -------------------------------------------------------------------------------- 1 | import keras.backend as K 2 | import tensorflow as tf 3 | 4 | 5 | def graph_conv_op(x, num_filters, graph_conv_filters, kernel): 6 | 7 | if len(x.get_shape()) == 2: 8 | conv_op = K.dot(graph_conv_filters, x) 9 | conv_op = tf.split(conv_op, num_filters, axis=0) 10 | conv_op = K.concatenate(conv_op, axis=1) 11 | elif len(x.get_shape()) == 3: 12 | conv_op = K.batch_dot(graph_conv_filters, x) 13 | conv_op = tf.split(conv_op, num_filters, axis=1) 14 | conv_op = K.concatenate(conv_op, axis=2) 15 | else: 16 | raise ValueError('x must be either 2 or 3 dimension tensor' 17 | 'Got input shape: ' + str(x.get_shape())) 18 | 19 | conv_out = K.dot(conv_op, kernel) 20 | return conv_out 21 | -------------------------------------------------------------------------------- /keras_dgl/layers/multi_graph_attention_cnn_layer.py: -------------------------------------------------------------------------------- 1 | from keras import activations, initializers, constraints 2 | from keras import regularizers 3 | import keras.backend as K 4 | from keras.engine.topology import Layer 5 | from keras.layers import Dropout, LeakyReLU, ELU 6 | from keras.engine import InputSpec 7 | import tensorflow as tf 8 | from .graph_ops import graph_conv_op 9 | 10 | 11 | class MultiGraphAttentionCNN(Layer): 12 | 13 | def __init__(self, 14 | output_dim, 15 | num_filters=None, 16 | num_attention_heads=1, 17 | attention_combine='concat', 18 | attention_dropout=0.5, 19 | activation=None, 20 | use_bias=False, 21 | kernel_initializer='glorot_uniform', 22 | bias_initializer='zeros', 23 | kernel_regularizer=None, 24 | bias_regularizer=None, 25 | activity_regularizer=None, 26 | kernel_constraint=None, 27 | bias_constraint=None, 28 | **kwargs): 29 | if attention_combine not in {'concat', 'average'}: 30 | raise ValueError('Possbile reduction methods: concat, average') 31 | 32 | super(MultiGraphAttentionCNN, self).__init__(**kwargs) 33 | 34 | self.output_dim = output_dim 35 | self.num_filters = num_filters 36 | 37 | self.num_attention_heads = num_attention_heads 38 | self.attention_combine = attention_combine 39 | self.attention_dropout = attention_dropout 40 | 41 | self.activation = activations.get(activation) 42 | self.use_bias = use_bias 43 | self.kernel_initializer = initializers.get(kernel_initializer) 44 | self.kernel_initializer.__name__ = kernel_initializer 45 | self.bias_initializer = initializers.get(bias_initializer) 46 | self.kernel_regularizer = regularizers.get(kernel_regularizer) 47 | self.bias_regularizer = regularizers.get(bias_regularizer) 48 | self.activity_regularizer = regularizers.get(activity_regularizer) 49 | self.kernel_constraint = constraints.get(kernel_constraint) 50 | self.bias_constraint = constraints.get(bias_constraint) 51 | self.supports_masking = False 52 | 53 | self.input_dim = None 54 | self.kernels = [] 55 | self.kernels_biases = [] 56 | self.attention_kernels = [] 57 | self.attention_kernels_biases = [] 58 | 59 | def build(self, input_shape): 60 | 61 | self.input_dim = input_shape[0][-1] 62 | 63 | if self.num_filters is not None: 64 | kernel_shape = (self.num_filters * self.input_dim, self.output_dim) 65 | else: 66 | kernel_shape = (self.input_dim, self.output_dim) 67 | 68 | attention_kernel_shape = (2 * self.output_dim, 1) 69 | 70 | for _ in range(self.num_attention_heads): 71 | 72 | kernel = self.add_weight(shape=kernel_shape, 73 | initializer=self.kernel_initializer, 74 | name='kernel', 75 | regularizer=self.kernel_regularizer, 76 | constraint=self.kernel_constraint) 77 | self.kernels.append(kernel) 78 | 79 | if self.use_bias: 80 | bias = self.add_weight(shape=(self.output_dim,), 81 | initializer=self.bias_initializer, 82 | name='bias', 83 | regularizer=self.bias_regularizer, 84 | constraint=self.bias_constraint) 85 | else: 86 | bias = None 87 | self.kernels_biases.append(bias) 88 | 89 | attention_kernel = self.add_weight(shape=attention_kernel_shape, 90 | initializer=self.kernel_initializer, 91 | name='attention_kernel', 92 | regularizer=self.kernel_regularizer, 93 | constraint=self.kernel_constraint) 94 | 95 | self.attention_kernels.append(attention_kernel) 96 | if self.use_bias: 97 | bias = self.add_weight(shape=(1,), 98 | initializer=self.bias_initializer, 99 | name='attention_bias', 100 | regularizer=self.bias_regularizer, 101 | constraint=self.bias_constraint) 102 | else: 103 | bias = None 104 | self.attention_kernels_biases.append(bias) 105 | 106 | self.built = True 107 | 108 | def call(self, inputs): 109 | 110 | outputs = [] 111 | 112 | for i in range(self.num_attention_heads): 113 | 114 | if self.num_filters is not None: 115 | conv_out = graph_conv_op(inputs[0], self.num_filters, inputs[2], self.kernels[i]) 116 | else: 117 | conv_out = K.dot(inputs[0], self.kernels[i]) 118 | 119 | if self.use_bias: 120 | conv_out = K.bias_add(conv_out, self.kernels_biases[i]) 121 | 122 | atten_conv_out_self = K.dot(conv_out, self.attention_kernels[i][:self.output_dim]) 123 | atten_conv_out_neigh = K.dot(conv_out, self.attention_kernels[i][self.output_dim:]) 124 | 125 | if self.use_bias: 126 | atten_conv_out_self = K.bias_add(atten_conv_out_self, self.attention_kernels_biases[i]) 127 | 128 | atten_coeff_matrix = atten_conv_out_self + tf.transpose(atten_conv_out_neigh, perm=[0, 2, 1]) 129 | atten_coeff_matrix = ELU(alpha=1.0)(atten_coeff_matrix) # can be replaced by LeakyReLU(alpha=0.2) as set in the paper 130 | 131 | mask = K.exp(inputs[1] * -10e9) * -10e9 132 | atten_coeff_matrix = atten_coeff_matrix + mask 133 | 134 | atten_coeff_matrix = K.softmax(atten_coeff_matrix) 135 | atten_coeff_matrix = Dropout(self.attention_dropout)(atten_coeff_matrix) 136 | 137 | node_feature_matrix = K.batch_dot(atten_coeff_matrix, conv_out) 138 | 139 | if self.attention_combine == 'concat' and self.activation is not None: 140 | node_feature_matrix = self.activation(node_feature_matrix) 141 | 142 | outputs.append(node_feature_matrix) 143 | 144 | if self.attention_combine == 'concat': 145 | output = K.concatenate(outputs) 146 | else: 147 | output = K.mean(K.stack(outputs), axis=0) 148 | if self.activation is not None: 149 | output = self.activation(output) 150 | 151 | return output 152 | 153 | def compute_output_shape(self, input_shape): 154 | 155 | if self.attention_combine == 'concat': 156 | actutal_output_dim = self.output_dim * self.num_attention_heads 157 | else: 158 | actutal_output_dim = self.output_dim 159 | 160 | output_shape = (input_shape[0][0], input_shape[0][1], actutal_output_dim) 161 | return output_shape 162 | 163 | def get_config(self): 164 | config = { 165 | 'output_dim': self.output_dim, 166 | 'num_filters': self.num_filters, 167 | 'num_attention_heads': self.num_attention_heads, 168 | 'attention_combine': self.attention_combine, 169 | 'attention_dropout': self.attention_dropout, 170 | 'activation': activations.serialize(self.activation), 171 | 'use_bias': self.use_bias, 172 | 'kernel_initializer': initializers.serialize(self.kernel_initializer), 173 | 'bias_initializer': initializers.serialize(self.bias_initializer), 174 | 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer), 175 | 'bias_regularizer': regularizers.serialize(self.bias_regularizer), 176 | 'activity_regularizer': regularizers.serialize(self.activity_regularizer), 177 | 'kernel_constraint': constraints.serialize(self.kernel_constraint), 178 | 'bias_constraint': constraints.serialize(self.bias_constraint) 179 | } 180 | base_config = super(MultiGraphAttentionCNN, self).get_config() 181 | return dict(list(base_config.items()) + list(config.items())) 182 | -------------------------------------------------------------------------------- /keras_dgl/layers/multi_graph_cnn_layer.py: -------------------------------------------------------------------------------- 1 | from keras import activations, initializers, constraints 2 | from keras import regularizers 3 | import keras.backend as K 4 | from keras.engine.topology import Layer 5 | import tensorflow as tf 6 | from .graph_ops import graph_conv_op 7 | 8 | 9 | class MultiGraphCNN(Layer): 10 | 11 | def __init__(self, 12 | output_dim, 13 | num_filters, 14 | activation=None, 15 | use_bias=True, 16 | kernel_initializer='glorot_uniform', 17 | bias_initializer='zeros', 18 | kernel_regularizer=None, 19 | bias_regularizer=None, 20 | activity_regularizer=None, 21 | kernel_constraint=None, 22 | bias_constraint=None, 23 | **kwargs): 24 | super(MultiGraphCNN, self).__init__(**kwargs) 25 | 26 | self.output_dim = output_dim 27 | self.num_filters = num_filters 28 | 29 | self.activation = activations.get(activation) 30 | self.use_bias = use_bias 31 | self.kernel_initializer = initializers.get(kernel_initializer) 32 | self.kernel_initializer.__name__ = kernel_initializer 33 | self.bias_initializer = initializers.get(bias_initializer) 34 | self.kernel_regularizer = regularizers.get(kernel_regularizer) 35 | self.bias_regularizer = regularizers.get(bias_regularizer) 36 | self.activity_regularizer = regularizers.get(activity_regularizer) 37 | self.kernel_constraint = constraints.get(kernel_constraint) 38 | self.bias_constraint = constraints.get(bias_constraint) 39 | 40 | def build(self, input_shape): 41 | 42 | if self.num_filters != int(input_shape[1][-2]/input_shape[1][-1]): 43 | raise ValueError('num_filters does not match with graph_conv_filters dimensions.') 44 | 45 | self.input_dim = input_shape[0][-1] 46 | kernel_shape = (self.num_filters * self.input_dim, self.output_dim) 47 | 48 | self.kernel = self.add_weight(shape=kernel_shape, 49 | initializer=self.kernel_initializer, 50 | name='kernel', 51 | regularizer=self.kernel_regularizer, 52 | constraint=self.kernel_constraint) 53 | if self.use_bias: 54 | self.bias = self.add_weight(shape=(self.output_dim,), 55 | initializer=self.bias_initializer, 56 | name='bias', 57 | regularizer=self.bias_regularizer, 58 | constraint=self.bias_constraint) 59 | else: 60 | self.bias = None 61 | 62 | self.built = True 63 | 64 | def call(self, inputs): 65 | 66 | output = graph_conv_op(inputs[0], self.num_filters, inputs[1], self.kernel) 67 | if self.use_bias: 68 | output = K.bias_add(output, self.bias) 69 | if self.activation is not None: 70 | output = self.activation(output) 71 | return output 72 | 73 | def compute_output_shape(self, input_shape): 74 | output_shape = (input_shape[0][0], input_shape[0][1], self.output_dim) 75 | return output_shape 76 | 77 | def get_config(self): 78 | config = { 79 | 'output_dim': self.output_dim, 80 | 'num_filters': self.num_filters, 81 | 'activation': activations.serialize(self.activation), 82 | 'use_bias': self.use_bias, 83 | 'kernel_initializer': initializers.serialize(self.kernel_initializer), 84 | 'bias_initializer': initializers.serialize(self.bias_initializer), 85 | 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer), 86 | 'bias_regularizer': regularizers.serialize(self.bias_regularizer), 87 | 'activity_regularizer': regularizers.serialize(self.activity_regularizer), 88 | 'kernel_constraint': constraints.serialize(self.kernel_constraint), 89 | 'bias_constraint': constraints.serialize(self.bias_constraint) 90 | } 91 | base_config = super(MultiGraphCNN, self).get_config() 92 | return dict(list(base_config.items()) + list(config.items())) 93 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Keras Deep Learning on Graphs 2 | site_url: http://vermaMachineLearning.github.io/keras-deep-graph-learning 3 | repo_url: https://github.com/vermaMachineLearning/keras-deep-graph-learning 4 | 5 | pages: 6 | - Home: index.md 7 | - Layers: 8 | - Graph Convolutional Layers: Layers/Convolution/graph_conv_layer.md 9 | - Graph Attention Layers: Layers/Attention/graph_attention_layer.md 10 | - Graph Recurrent Layers: Layers/Recurrent/graph_conv_recurrent_layer.md 11 | - Graph Capsule CNN Layers: Layers/Graph Capsule Neural Network/graph_capsule_cnn.md 12 | - Graph Neural Network Layers: Layers/Graph Neural Network/graph_neural_networks.md 13 | - Graph Convolution Filters: Filters/graph_conv_filters.md 14 | - About: about.md 15 | theme: readthedocs 16 | 17 | #the third-party extenison mdx_math.py comes with a setup.py script 18 | # Thank you Dmitry Shachnev (https://github.com/mitya57/python-markdown-math) 19 | markdown_extensions: 20 | - mdx_math: 21 | enable_dollar_delimiter: True #for use of inline $..$ 22 | 23 | # Required to load the MathJax javascript for the mdx_math.py 24 | extra_javascript: ['https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML'] -------------------------------------------------------------------------------- /site/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 89 | 90 |
91 | 92 | 93 | 97 | 98 | 99 |
100 |
101 |
102 |
    103 |
  • Docs »
  • 104 | 105 | 106 |
  • 107 | 108 |
  • 109 |
110 |
111 |
112 |
113 |
114 | 115 | 116 |

404

117 | 118 |

Page not found

119 | 120 | 121 |
122 |
123 |
124 | 125 | 126 |
127 | 128 |
129 | 130 | 131 |
132 | 133 | Built with MkDocs using a theme provided by Read the Docs. 134 |
135 | 136 |
137 |
138 | 139 |
140 | 141 |
142 | 143 |
144 | 145 | 146 | GitHub 147 | 148 | 149 | 150 | 151 |
152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /site/Filters/graph_conv_filters/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Graph Convolution Filters - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 114 | 115 |
116 | 117 | 118 | 122 | 123 | 124 |
125 |
126 |
127 |
    128 |
  • Docs »
  • 129 | 130 | 131 | 132 |
  • Graph Convolution Filters
  • 133 |
  • 134 | 135 | Edit on GitHub 137 | 138 |
  • 139 |
140 |
141 |
142 |
143 |
144 | 145 |

Standard Polynomial

146 |
standard_poly(A, poly_degree=3)
147 | 
148 | 149 |

Computes Standard Polynomial function. Current implementation complexity is .

150 |

Inputs:

151 |
    152 |
  • A : 2D Tensor, graph adjacency matrix or (normalized) Laplacian.
  • 153 |
  • poly_degree: Integer, polynomial degree (default=1).
  • 154 |
155 |

Outputs:

156 |
    157 |
  • 3D Tensor, containing standard polynomial powers of graph adjacency matrix or Laplacian.
  • 158 |
159 |
160 |

Chebyshev Polynomial

161 |
chebyshev_poly(A, poly_degree=3)
162 | 
163 | 164 |

Computes Chebyshev Polynomial function. Current implementation complexity is .

165 |

Inputs:

166 |
    167 |
  • A : 2D Tensor, graph adjacency matrix or (normalized) Laplacian.
  • 168 |
  • poly_degree: Integer, polynomial degree (default=1).
  • 169 |
170 |

Outputs:

171 |
    172 |
  • 3D Tensor, containing chebyshev polynomial powers of graph adjacency matrix or Laplacian.
  • 173 |
174 |

References: Defferrard, Michaël, Xavier Bresson, and Pierre Vandergheynst. "Convolutional neural networks on graphs with fast localized spectral filtering." In Advances in Neural Information Processing Systems, pp. 3844-3852. 2016.

175 |
176 |

Random Walk Polynomial

177 |
chebyshev_poly(A, poly_degree=3)
178 | 
179 | 180 |

Computes Random Walk Polynomial function. Current implementation complexity is .

181 |

Inputs:

182 |
    183 |
  • A : 2D Tensor, graph adjacency matrix or (normalized) Laplacian.
  • 184 |
  • poly_degree: Integer, polynomial degree (default=1).
  • 185 |
186 |

Outputs:

187 |
    188 |
  • 3D Tensor, containing chebyshev polynomial powers of graph adjacency matrix or Laplacian.
  • 189 |
190 |
191 |

Cayley Polynomial

192 |
cayley_poly(A, poly_degree=3)
193 | 
194 | 195 |

Computes Cayley Polynomial function. Current implementation complexity is .

196 |

Inputs:

197 |
    198 |
  • A : 2D Tensor, graph adjacency matrix or (normalized) Laplacian.
  • 199 |
  • poly_degree: Integer, polynomial degree (default=1).
  • 200 |
201 |

Outputs:

202 |
    203 |
  • 3D Tensor, containing cayley polynomial powers of graph adjacency matrix or Laplacian.
  • 204 |
205 |

References: Levie, Ron, Federico Monti, Xavier Bresson, and Michael M. Bronstein. "Cayleynets: Graph convolutional neural networks with complex rational spectral filters." arXiv preprint arXiv:1705.07664 (2017).

206 |
207 |

Combine Polynomial

208 |
combine_poly(A, B, poly_degree=3)
209 | 
210 | 211 |

Computes combination of polynomial function.

212 |

Inputs:

213 |
    214 |
  • A : 2D Tensor, graph adjacency or (normalized) Laplacian or cayley matrix.
  • 215 |
  • B : 2D Tensor, graph adjacency matrix or (normalized) Laplacian or cayley matrix.
  • 216 |
  • poly_degree: Integer, polynomial degree (default=1).
  • 217 |
218 |

Outputs:

219 |
    220 |
  • 3D Tensor, containing combine polynomial powers of graph adjacency or Laplacian or cayley matrix.
  • 221 |
222 | 223 |
224 |
225 |
226 | 227 | 235 | 236 | 237 |
238 | 239 |
240 | 241 | 242 |
243 | 244 | Built with MkDocs using a theme provided by Read the Docs. 245 |
246 | 247 |
248 |
249 | 250 |
251 | 252 |
253 | 254 |
255 | 256 | 257 | GitHub 258 | 259 | 260 | « Previous 261 | 262 | 263 | Next » 264 | 265 | 266 |
267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /site/Layers/Graph Capsule Neural Network/graph_capsule_cnn/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Graph Capsule CNN Layers - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 99 | 100 |
101 | 102 | 103 | 107 | 108 | 109 |
110 |
111 |
112 |
    113 |
  • Docs »
  • 114 | 115 | 116 | 117 |
  • Layers »
  • 118 | 119 | 120 | 121 |
  • Graph Capsule CNN Layers
  • 122 |
  • 123 | 124 | Edit on GitHub 126 | 127 |
  • 128 |
129 |
130 |
131 |
132 |
133 | 134 |

TBD

135 | 136 |
137 |
138 |
139 | 140 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 |
156 | 157 | Built with MkDocs using a theme provided by Read the Docs. 158 |
159 | 160 |
161 |
162 | 163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | GitHub 171 | 172 | 173 | « Previous 174 | 175 | 176 | Next » 177 | 178 | 179 |
180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /site/Layers/Graph Neural Network/graph_neural_networks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Graph Neural Network Layers - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 99 | 100 |
101 | 102 | 103 | 107 | 108 | 109 |
110 |
111 |
112 |
    113 |
  • Docs »
  • 114 | 115 | 116 | 117 |
  • Layers »
  • 118 | 119 | 120 | 121 |
  • Graph Neural Network Layers
  • 122 |
  • 123 | 124 | Edit on GitHub 126 | 127 |
  • 128 |
129 |
130 |
131 |
132 |
133 | 134 |

TBD

135 | 136 |
137 |
138 |
139 | 140 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 |
156 | 157 | Built with MkDocs using a theme provided by Read the Docs. 158 |
159 | 160 |
161 |
162 | 163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | GitHub 171 | 172 | 173 | « Previous 174 | 175 | 176 | Next » 177 | 178 | 179 |
180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /site/Layers/Recurrent/graph_conv_recurrent_layer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Graph Recurrent Layers - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 102 | 103 |
104 | 105 | 106 | 110 | 111 | 112 |
113 |
114 |
115 |
    116 |
  • Docs »
  • 117 | 118 | 119 | 120 |
  • Layers »
  • 121 | 122 | 123 | 124 |
  • Graph Recurrent Layers
  • 125 |
  • 126 | 127 | Edit on GitHub 129 | 130 |
  • 131 |
132 |
133 |
134 |
135 |
136 | 137 |

[source]

138 |

GraphConvLSTM

139 |
GraphConvLSTM(output_dim, graph_conv_filters, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
140 | 
141 | 142 |

1D convolution layer (e.g. temporal convolution).

143 |

This layer creates a convolution kernel that is convolved 144 | with the layer input over a single spatial (or temporal) dimension 145 | to produce a tensor of outputs. 146 | If use_bias is True, a bias vector is created and added to the outputs. 147 | Finally, if activation is not None, 148 | it is applied to the outputs as well.

149 |

When using this layer as the first layer in a model, 150 | provide an input_shape argument 151 | (tuple of integers or None, e.g. 152 | (10, 128) for sequences of 10 vectors of 128-dimensional vectors, 153 | or (None, 128) for variable-length sequences of 128-dimensional vectors.

154 |

Arguments

155 |
    156 |
  • output_dim: Positive integer, dimensionality of each graph node output space (or dimension of graph node embedding).
  • 157 |
  • graph_conv_filters: 3D Tensor, the dimensionality of the output space 158 | (i.e. the number output of filters in the convolution).
  • 159 |
  • activation: Activation function to use 160 | (see activations). 161 | If you don't specify anything, no activation is applied 162 | (ie. "linear" activation: a(x) = x).
  • 163 |
  • use_bias: Boolean, whether the layer uses a bias vector.
  • 164 |
  • kernel_initializer: Initializer for the kernel weights matrix 165 | (see initializers).
  • 166 |
  • bias_initializer: Initializer for the bias vector 167 | (see initializers).
  • 168 |
  • kernel_regularizer: Regularizer function applied to 169 | the kernel weights matrix 170 | (see regularizer).
  • 171 |
  • bias_regularizer: Regularizer function applied to the bias vector 172 | (see regularizer).
  • 173 |
  • activity_regularizer: Regularizer function applied to 174 | the output of the layer (its "activation"). 175 | (see regularizer).
  • 176 |
  • kernel_constraint: Constraint function applied to the kernel matrix 177 | (see constraints).
  • 178 |
  • bias_constraint: Constraint function applied to the bias vector 179 | (see constraints).
  • 180 |
181 |

Input shapes

182 |
    183 |
  • 4D tensor with shape: (samples, timestep, num_graph_nodes, input_dim)
  • 184 |
185 |

Output shape

186 |
    187 |
  • if return_sequences
    188 | 4D tensor with shape: (samples, timestep, num_graph_nodes, output_dim)
  • 189 |
  • else
    190 | 4D tensor with shape: (samples, num_graph_nodes, output_dim)
  • 191 |
192 | 193 |
194 |
195 |
196 | 197 | 205 | 206 | 207 |
208 | 209 |
210 | 211 | 212 |
213 | 214 | Built with MkDocs using a theme provided by Read the Docs. 215 |
216 | 217 |
218 |
219 | 220 |
221 | 222 |
223 | 224 |
225 | 226 | 227 | GitHub 228 | 229 | 230 | « Previous 231 | 232 | 233 | Next » 234 | 235 | 236 |
237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /site/about/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | About - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 102 | 103 |
104 | 105 | 106 | 110 | 111 | 112 |
113 |
114 |
115 |
    116 |
  • Docs »
  • 117 | 118 | 119 | 120 |
  • About
  • 121 |
  • 122 | 123 | Edit on GitHub 125 | 126 |
  • 127 |
128 |
129 |
130 |
131 |
132 | 133 |

Author

134 |

Saurabh Verma, PhD Student at University of Minnesota Twin Cities.

135 |

About me: http://www-users.cs.umn.edu/~verma076/

136 |

Acknowledgments: Some part of the code has been borrowed from the original authors implementation and others. List of these authors are TBD.

137 | 138 |
139 |
140 |
141 | 142 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 |
156 | 157 | Built with MkDocs using a theme provided by Read the Docs. 158 |
159 | 160 |
161 |
162 | 163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | GitHub 171 | 172 | 173 | « Previous 174 | 175 | 176 | 177 |
178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /site/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | This is the GitHub theme for highlight.js 3 | 4 | github.com style (c) Vasily Polovnyov 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | color: #333; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #333; 31 | font-weight: bold; 32 | } 33 | 34 | .hljs-number, 35 | .hljs-hexcolor, 36 | .ruby .hljs-constant { 37 | color: #008080; 38 | } 39 | 40 | .hljs-string, 41 | .hljs-tag .hljs-value, 42 | .hljs-phpdoc, 43 | .hljs-dartdoc, 44 | .tex .hljs-formula { 45 | color: #d14; 46 | } 47 | 48 | .hljs-title, 49 | .hljs-id, 50 | .scss .hljs-preprocessor { 51 | color: #900; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-list .hljs-keyword, 56 | .hljs-subst { 57 | font-weight: normal; 58 | } 59 | 60 | .hljs-class .hljs-title, 61 | .hljs-type, 62 | .vhdl .hljs-literal, 63 | .tex .hljs-command { 64 | color: #458; 65 | font-weight: bold; 66 | } 67 | 68 | .hljs-tag, 69 | .hljs-tag .hljs-title, 70 | .hljs-rule .hljs-property, 71 | .django .hljs-tag .hljs-keyword { 72 | color: #000080; 73 | font-weight: normal; 74 | } 75 | 76 | .hljs-attribute, 77 | .hljs-variable, 78 | .lisp .hljs-body, 79 | .hljs-name { 80 | color: #008080; 81 | } 82 | 83 | .hljs-regexp { 84 | color: #009926; 85 | } 86 | 87 | .hljs-symbol, 88 | .ruby .hljs-symbol .hljs-string, 89 | .lisp .hljs-keyword, 90 | .clojure .hljs-keyword, 91 | .scheme .hljs-keyword, 92 | .tex .hljs-special, 93 | .hljs-prompt { 94 | color: #990073; 95 | } 96 | 97 | .hljs-built_in { 98 | color: #0086b3; 99 | } 100 | 101 | .hljs-preprocessor, 102 | .hljs-pragma, 103 | .hljs-pi, 104 | .hljs-doctype, 105 | .hljs-shebang, 106 | .hljs-cdata { 107 | color: #999; 108 | font-weight: bold; 109 | } 110 | 111 | .hljs-deletion { 112 | background: #fdd; 113 | } 114 | 115 | .hljs-addition { 116 | background: #dfd; 117 | } 118 | 119 | .diff .hljs-change { 120 | background: #0086b3; 121 | } 122 | 123 | .hljs-chunk { 124 | color: #aaa; 125 | } 126 | -------------------------------------------------------------------------------- /site/css/highlight_original.css: -------------------------------------------------------------------------------- 1 | /* 2 | This is the GitHub theme for highlight.js 3 | 4 | github.com style (c) Vasily Polovnyov 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | color: #333; 12 | -webkit-text-size-adjust: none; 13 | } 14 | 15 | .hljs-comment, 16 | .diff .hljs-header, 17 | .hljs-javadoc { 18 | color: #998; 19 | font-style: italic; 20 | } 21 | 22 | .hljs-keyword, 23 | .css .rule .hljs-keyword, 24 | .hljs-winutils, 25 | .nginx .hljs-title, 26 | .hljs-subst, 27 | .hljs-request, 28 | .hljs-status { 29 | color: #333; 30 | font-weight: bold; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #008080; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #d14; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rule .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body, 78 | .hljs-name { 79 | color: #008080; 80 | } 81 | 82 | .hljs-regexp { 83 | color: #009926; 84 | } 85 | 86 | .hljs-symbol, 87 | .ruby .hljs-symbol .hljs-string, 88 | .lisp .hljs-keyword, 89 | .clojure .hljs-keyword, 90 | .scheme .hljs-keyword, 91 | .tex .hljs-special, 92 | .hljs-prompt { 93 | color: #990073; 94 | } 95 | 96 | .hljs-built_in { 97 | color: #0086b3; 98 | } 99 | 100 | .hljs-preprocessor, 101 | .hljs-pragma, 102 | .hljs-pi, 103 | .hljs-doctype, 104 | .hljs-shebang, 105 | .hljs-cdata { 106 | color: #999; 107 | font-weight: bold; 108 | } 109 | 110 | .hljs-deletion { 111 | background: #fdd; 112 | } 113 | 114 | .hljs-addition { 115 | background: #dfd; 116 | } 117 | 118 | .diff .hljs-change { 119 | background: #0086b3; 120 | } 121 | 122 | .hljs-chunk { 123 | color: #aaa; 124 | } 125 | -------------------------------------------------------------------------------- /site/css/theme_extra.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Tweak the overal size to better match RTD. 3 | */ 4 | body { 5 | font-size: 90%; 6 | } 7 | 8 | .keras-logo { 9 | max-height: 55px; 10 | width: 100%; 11 | background: #d00000; 12 | font-size: 150%; 13 | color: white; 14 | font-family: "Source Sans Pro", "ff-tisa-web-pro", "Georgia", Arial, sans-serif; 15 | } 16 | 17 | .keras-logo-img { 18 | max-width: 45px; 19 | margin: 10px; 20 | } 21 | 22 | h1, h2, h3, h4, h5, h6, legend { 23 | font-family: "Source Sans Pro", "ff-tisa-web-pro", "Georgia", Arial, sans-serif;, 24 | } 25 | 26 | /* 27 | * Sphinx doesn't have support for section dividers like we do in 28 | * MkDocs, this styles the section titles in the nav 29 | * 30 | * https://github.com/mkdocs/mkdocs/issues/175 31 | */ 32 | .wy-menu-vertical span { 33 | line-height: 18px; 34 | padding: 0.4045em 1.618em; 35 | display: block; 36 | position: relative; 37 | font-size: 90%; 38 | color: #838383; 39 | } 40 | 41 | .wy-menu-vertical .subnav a { 42 | padding: 0.4045em 2.427em; 43 | } 44 | 45 | /* 46 | * Long navigations run off the bottom of the screen as the nav 47 | * area doesn't scroll. 48 | * 49 | * https://github.com/mkdocs/mkdocs/pull/202 50 | */ 51 | .wy-nav-side { 52 | height: 100%; 53 | overflow-y: auto; 54 | } 55 | 56 | .wy-nav-content { 57 | max-width: 900px; 58 | } 59 | 60 | /* 61 | * readthedocs theme hides nav items when the window height is 62 | * too small to contain them. 63 | * 64 | * https://github.com/mkdocs/mkdocs/issues/#348 65 | */ 66 | .wy-menu-vertical ul { 67 | margin-bottom: 2em; 68 | } 69 | 70 | /* 71 | * Fix wrapping in the code highlighting 72 | * 73 | * https://github.com/mkdocs/mkdocs/issues/233 74 | */ 75 | code { 76 | white-space: pre; 77 | font-size: 90%; 78 | color: #9E0F00; 79 | background: #FFFAFA; 80 | } 81 | 82 | /* 83 | * Wrap inline code samples otherwise they shoot of the side and 84 | * can't be read at all. 85 | * 86 | * https://github.com/mkdocs/mkdocs/issues/313 87 | */ 88 | p code { 89 | word-wrap: break-word; 90 | } 91 | 92 | /* 93 | * The CSS classes from highlight.js seem to clash with the 94 | * ReadTheDocs theme causing some code to be incorrectly made 95 | * bold and italic. 96 | * 97 | * https://github.com/mkdocs/mkdocs/issues/411 98 | */ 99 | code.cs, code.c { 100 | font-weight: inherit; 101 | font-style: inherit; 102 | } 103 | 104 | /* 105 | * Fix some issues with the theme and non-highlighted code 106 | * samples. Without and highlighting styles attached the 107 | * formatting is broken. 108 | * 109 | * https://github.com/mkdocs/mkdocs/issues/319 110 | */ 111 | .no-highlight { 112 | display: block; 113 | padding: 0.5em; 114 | color: #333; 115 | } 116 | 117 | 118 | /* 119 | * Additions specific to the search functionality provided by MkDocs 120 | */ 121 | 122 | #mkdocs-search-results article h3 123 | { 124 | margin-top: 23px; 125 | border-top: 1px solid #E1E4E5; 126 | padding-top: 24px; 127 | } 128 | 129 | #mkdocs-search-results article:first-child h3 { 130 | border-top: none; 131 | } 132 | 133 | #mkdocs-search-query{ 134 | width: 100%; 135 | border-radius: 50px; 136 | padding: 6px 12px; 137 | border-color: #D1D4D5; 138 | } 139 | 140 | .wy-menu-vertical li ul { 141 | display: inherit; 142 | } 143 | 144 | .wy-menu-vertical li ul.subnav ul.subnav{ 145 | padding-left: 1em; 146 | } 147 | -------------------------------------------------------------------------------- /site/css/theme_extra_original.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Sphinx doesn't have support for section dividers like we do in 3 | * MkDocs, this styles the section titles in the nav 4 | * 5 | * https://github.com/mkdocs/mkdocs/issues/175 6 | */ 7 | .wy-menu-vertical span { 8 | line-height: 18px; 9 | padding: 0.4045em 1.618em; 10 | display: block; 11 | position: relative; 12 | font-size: 90%; 13 | color: #838383; 14 | } 15 | 16 | .wy-menu-vertical .subnav a { 17 | padding: 0.4045em 2.427em; 18 | } 19 | 20 | /* 21 | * Long navigations run off the bottom of the screen as the nav 22 | * area doesn't scroll. 23 | * 24 | * https://github.com/mkdocs/mkdocs/pull/202 25 | * 26 | * Builds upon pull 202 https://github.com/mkdocs/mkdocs/pull/202 27 | * to make toc scrollbar end before navigations buttons to not be overlapping. 28 | */ 29 | .wy-nav-side { 30 | height: calc(100% - 45px); 31 | overflow-y: auto; 32 | min-height: 0; 33 | } 34 | 35 | .rst-versions{ 36 | border-top: 0; 37 | height: 45px; 38 | } 39 | 40 | @media screen and (max-width: 768px) { 41 | .wy-nav-side { 42 | height: 100%; 43 | } 44 | } 45 | 46 | /* 47 | * readthedocs theme hides nav items when the window height is 48 | * too small to contain them. 49 | * 50 | * https://github.com/mkdocs/mkdocs/issues/#348 51 | */ 52 | .wy-menu-vertical ul { 53 | margin-bottom: 2em; 54 | } 55 | 56 | /* 57 | * Wrap inline code samples otherwise they shoot of the side and 58 | * can't be read at all. 59 | * 60 | * https://github.com/mkdocs/mkdocs/issues/313 61 | * https://github.com/mkdocs/mkdocs/issues/233 62 | * https://github.com/mkdocs/mkdocs/issues/834 63 | */ 64 | code { 65 | white-space: pre-wrap; 66 | word-wrap: break-word; 67 | padding: 2px 5px; 68 | } 69 | 70 | /** 71 | * Make code blocks display as blocks and give them the appropriate 72 | * font size and padding. 73 | * 74 | * https://github.com/mkdocs/mkdocs/issues/855 75 | * https://github.com/mkdocs/mkdocs/issues/834 76 | * https://github.com/mkdocs/mkdocs/issues/233 77 | */ 78 | pre code { 79 | white-space: pre; 80 | word-wrap: normal; 81 | display: block; 82 | padding: 12px; 83 | font-size: 12px; 84 | } 85 | 86 | /* 87 | * Fix link colors when the link text is inline code. 88 | * 89 | * https://github.com/mkdocs/mkdocs/issues/718 90 | */ 91 | a code { 92 | color: #2980B9; 93 | } 94 | a:hover code { 95 | color: #3091d1; 96 | } 97 | a:visited code { 98 | color: #9B59B6; 99 | } 100 | 101 | /* 102 | * The CSS classes from highlight.js seem to clash with the 103 | * ReadTheDocs theme causing some code to be incorrectly made 104 | * bold and italic. 105 | * 106 | * https://github.com/mkdocs/mkdocs/issues/411 107 | */ 108 | pre .cs, pre .c { 109 | font-weight: inherit; 110 | font-style: inherit; 111 | } 112 | 113 | /* 114 | * Fix some issues with the theme and non-highlighted code 115 | * samples. Without and highlighting styles attached the 116 | * formatting is broken. 117 | * 118 | * https://github.com/mkdocs/mkdocs/issues/319 119 | */ 120 | .no-highlight { 121 | display: block; 122 | padding: 0.5em; 123 | color: #333; 124 | } 125 | 126 | 127 | /* 128 | * Additions specific to the search functionality provided by MkDocs 129 | */ 130 | 131 | .search-results article { 132 | margin-top: 23px; 133 | border-top: 1px solid #E1E4E5; 134 | padding-top: 24px; 135 | } 136 | 137 | .search-results article:first-child { 138 | border-top: none; 139 | } 140 | 141 | form .search-query { 142 | width: 100%; 143 | border-radius: 50px; 144 | padding: 6px 12px; /* csslint allow: box-model */ 145 | border-color: #D1D4D5; 146 | } 147 | 148 | .wy-menu-vertical li ul { 149 | display: inherit; 150 | } 151 | 152 | .wy-menu-vertical li ul.subnav ul.subnav{ 153 | padding-left: 1em; 154 | } 155 | 156 | .wy-menu-vertical .subnav li.current > a { 157 | padding-left: 2.42em; 158 | } 159 | .wy-menu-vertical .subnav li.current > ul li a { 160 | padding-left: 3.23em; 161 | } 162 | 163 | /* 164 | * Improve inline code blocks within admonitions. 165 | * 166 | * https://github.com/mkdocs/mkdocs/issues/656 167 | */ 168 | .admonition code { 169 | color: #404040; 170 | border: 1px solid #c7c9cb; 171 | border: 1px solid rgba(0, 0, 0, 0.2); 172 | background: #f8fbfd; 173 | background: rgba(255, 255, 255, 0.7); 174 | } 175 | 176 | /* 177 | * Account for wide tables which go off the side. 178 | * Override borders to avoid wierdness on narrow tables. 179 | * 180 | * https://github.com/mkdocs/mkdocs/issues/834 181 | * https://github.com/mkdocs/mkdocs/pull/1034 182 | */ 183 | .rst-content .section .docutils { 184 | width: 100%; 185 | overflow: auto; 186 | display: block; 187 | border: none; 188 | } 189 | 190 | td, th { 191 | border: 1px solid #e1e4e5 !important; /* csslint allow: important */ 192 | border-collapse: collapse; 193 | } 194 | 195 | -------------------------------------------------------------------------------- /site/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/site/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /site/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/site/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /site/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/site/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /site/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/site/img/favicon.ico -------------------------------------------------------------------------------- /site/img/favicon_orginal.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vermaMachineLearning/keras-deep-graph-learning/899e85282981b8d5289df4f22f119fcbeaf5d657/site/img/favicon_orginal.ico -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Home - Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 102 | 103 |
104 | 105 | 106 | 110 | 111 | 112 |
113 |
114 |
115 |
    116 |
  • Docs »
  • 117 | 118 | 119 | 120 |
  • Home
  • 121 |
  • 122 | 123 | Edit on GitHub 125 | 126 |
  • 127 |
128 |
129 |
130 |
131 |
132 | 133 |

Welcome to Keras Deep Learning on Graphs (Keras-DGL)

134 |

The aim of this keras extension is to provide Sequential and Functional API for performing deep learning tasks on graphs. Specifically, Keras-DGL provides implementation for these particular type of layers,

135 |
    136 |
  • Graph Convolutional Neural Networks (GraphCNN).
  • 137 |
  • Graph Attention Convolutional Neural Networks (GraphAttentionCNN).
  • 138 |
  • Graph Convolutional Recurrent Neural Networks (GraphConvLSTM).
  • 139 |
  • Graph Capsule Convolutional Recurrent Neural Networks (GraphCapsuleCNN) TBD.
  • 140 |
  • Graph Message Passing Neural Networks (GraphNeuralNetworks) TBD.
  • 141 |
  • Keras-DGL also contains implementation of various graph convolutional filters TBD.
  • 142 |
143 | 144 |
145 |
146 |
147 | 148 | 154 | 155 | 156 |
157 | 158 |
159 | 160 | 161 |
162 | 163 | Built with MkDocs using a theme provided by Read the Docs. 164 |
165 | 166 |
167 |
168 | 169 |
170 | 171 |
172 | 173 |
174 | 175 | 176 | GitHub 177 | 178 | 179 | 180 | Next » 181 | 182 | 183 |
184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 197 | -------------------------------------------------------------------------------- /site/js/modernizr-2.8.3.min.js: -------------------------------------------------------------------------------- 1 | window.Modernizr=function(e,t,n){function r(e){b.cssText=e}function o(e,t){return r(S.join(e+";")+(t||""))}function a(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function c(e,t){for(var r in e){var o=e[r];if(!i(o,"-")&&b[o]!==n)return"pfx"==t?o:!0}return!1}function s(e,t,r){for(var o in e){var i=t[e[o]];if(i!==n)return r===!1?e[o]:a(i,"function")?i.bind(r||t):i}return!1}function u(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),o=(e+" "+k.join(r+" ")+r).split(" ");return a(t,"string")||a(t,"undefined")?c(o,t):(o=(e+" "+T.join(r+" ")+r).split(" "),s(o,t,n))}function l(){p.input=function(n){for(var r=0,o=n.length;o>r;r++)j[n[r]]=!!(n[r]in E);return j.list&&(j.list=!(!t.createElement("datalist")||!e.HTMLDataListElement)),j}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),p.inputtypes=function(e){for(var r,o,a,i=0,c=e.length;c>i;i++)E.setAttribute("type",o=e[i]),r="text"!==E.type,r&&(E.value=x,E.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&E.style.WebkitAppearance!==n?(g.appendChild(E),a=t.defaultView,r=a.getComputedStyle&&"textfield"!==a.getComputedStyle(E,null).WebkitAppearance&&0!==E.offsetHeight,g.removeChild(E)):/^(search|tel)$/.test(o)||(r=/^(url|email)$/.test(o)?E.checkValidity&&E.checkValidity()===!1:E.value!=x)),P[e[i]]=!!r;return P}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d,f,m="2.8.3",p={},h=!0,g=t.documentElement,v="modernizr",y=t.createElement(v),b=y.style,E=t.createElement("input"),x=":)",w={}.toString,S=" -webkit- -moz- -o- -ms- ".split(" "),C="Webkit Moz O ms",k=C.split(" "),T=C.toLowerCase().split(" "),N={svg:"http://www.w3.org/2000/svg"},M={},P={},j={},$=[],D=$.slice,F=function(e,n,r,o){var a,i,c,s,u=t.createElement("div"),l=t.body,d=l||t.createElement("body");if(parseInt(r,10))for(;r--;)c=t.createElement("div"),c.id=o?o[r]:v+(r+1),u.appendChild(c);return a=["­",'"].join(""),u.id=v,(l?u:d).innerHTML+=a,d.appendChild(u),l||(d.style.background="",d.style.overflow="hidden",s=g.style.overflow,g.style.overflow="hidden",g.appendChild(d)),i=n(u,e),l?u.parentNode.removeChild(u):(d.parentNode.removeChild(d),g.style.overflow=s),!!i},z=function(t){var n=e.matchMedia||e.msMatchMedia;if(n)return n(t)&&n(t).matches||!1;var r;return F("@media "+t+" { #"+v+" { position: absolute; } }",function(t){r="absolute"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).position}),r},A=function(){function e(e,o){o=o||t.createElement(r[e]||"div"),e="on"+e;var i=e in o;return i||(o.setAttribute||(o=t.createElement("div")),o.setAttribute&&o.removeAttribute&&(o.setAttribute(e,""),i=a(o[e],"function"),a(o[e],"undefined")||(o[e]=n),o.removeAttribute(e))),o=null,i}var r={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return e}(),L={}.hasOwnProperty;f=a(L,"undefined")||a(L.call,"undefined")?function(e,t){return t in e&&a(e.constructor.prototype[t],"undefined")}:function(e,t){return L.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=D.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=t.prototype;var a=new o,i=t.apply(a,n.concat(D.call(arguments)));return Object(i)===i?i:a}return t.apply(e,n.concat(D.call(arguments)))};return r}),M.flexbox=function(){return u("flexWrap")},M.flexboxlegacy=function(){return u("boxDirection")},M.canvas=function(){var e=t.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},M.canvastext=function(){return!(!p.canvas||!a(t.createElement("canvas").getContext("2d").fillText,"function"))},M.webgl=function(){return!!e.WebGLRenderingContext},M.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:F(["@media (",S.join("touch-enabled),("),v,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},M.geolocation=function(){return"geolocation"in navigator},M.postmessage=function(){return!!e.postMessage},M.websqldatabase=function(){return!!e.openDatabase},M.indexedDB=function(){return!!u("indexedDB",e)},M.hashchange=function(){return A("hashchange",e)&&(t.documentMode===n||t.documentMode>7)},M.history=function(){return!(!e.history||!history.pushState)},M.draganddrop=function(){var e=t.createElement("div");return"draggable"in e||"ondragstart"in e&&"ondrop"in e},M.websockets=function(){return"WebSocket"in e||"MozWebSocket"in e},M.rgba=function(){return r("background-color:rgba(150,255,150,.5)"),i(b.backgroundColor,"rgba")},M.hsla=function(){return r("background-color:hsla(120,40%,100%,.5)"),i(b.backgroundColor,"rgba")||i(b.backgroundColor,"hsla")},M.multiplebgs=function(){return r("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(b.background)},M.backgroundsize=function(){return u("backgroundSize")},M.borderimage=function(){return u("borderImage")},M.borderradius=function(){return u("borderRadius")},M.boxshadow=function(){return u("boxShadow")},M.textshadow=function(){return""===t.createElement("div").style.textShadow},M.opacity=function(){return o("opacity:.55"),/^0.55$/.test(b.opacity)},M.cssanimations=function(){return u("animationName")},M.csscolumns=function(){return u("columnCount")},M.cssgradients=function(){var e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",n="linear-gradient(left top,#9f9, white);";return r((e+"-webkit- ".split(" ").join(t+e)+S.join(n+e)).slice(0,-e.length)),i(b.backgroundImage,"gradient")},M.cssreflections=function(){return u("boxReflect")},M.csstransforms=function(){return!!u("transform")},M.csstransforms3d=function(){var e=!!u("perspective");return e&&"webkitPerspective"in g.style&&F("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},M.csstransitions=function(){return u("transition")},M.fontface=function(){var e;return F('@font-face {font-family:"font";src:url("https://")}',function(n,r){var o=t.getElementById("smodernizr"),a=o.sheet||o.styleSheet,i=a?a.cssRules&&a.cssRules[0]?a.cssRules[0].cssText:a.cssText||"":"";e=/src/i.test(i)&&0===i.indexOf(r.split(" ")[0])}),e},M.generatedcontent=function(){var e;return F(["#",v,"{font:0/0 a}#",v,':after{content:"',x,'";visibility:hidden;font:3px/1 a}'].join(""),function(t){e=t.offsetHeight>=3}),e},M.video=function(){var e=t.createElement("video"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),n.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),n.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""))}catch(r){}return n},M.audio=function(){var e=t.createElement("audio"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),n.mp3=e.canPlayType("audio/mpeg;").replace(/^no$/,""),n.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),n.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(r){}return n},M.localstorage=function(){try{return localStorage.setItem(v,v),localStorage.removeItem(v),!0}catch(e){return!1}},M.sessionstorage=function(){try{return sessionStorage.setItem(v,v),sessionStorage.removeItem(v),!0}catch(e){return!1}},M.webworkers=function(){return!!e.Worker},M.applicationcache=function(){return!!e.applicationCache},M.svg=function(){return!!t.createElementNS&&!!t.createElementNS(N.svg,"svg").createSVGRect},M.inlinesvg=function(){var e=t.createElement("div");return e.innerHTML="",(e.firstChild&&e.firstChild.namespaceURI)==N.svg},M.smil=function(){return!!t.createElementNS&&/SVGAnimate/.test(w.call(t.createElementNS(N.svg,"animate")))},M.svgclippaths=function(){return!!t.createElementNS&&/SVGClipPath/.test(w.call(t.createElementNS(N.svg,"clipPath")))};for(var H in M)f(M,H)&&(d=H.toLowerCase(),p[d]=M[H](),$.push((p[d]?"":"no-")+d));return p.input||l(),p.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&p.addTest(r,e[r]);else{if(e=e.toLowerCase(),p[e]!==n)return p;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(g.className+=" "+(t?"":"no-")+e),p[e]=t}return p},r(""),y=E=null,function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=y.elements;return"string"==typeof e?e.split(" "):e}function o(e){var t=v[e[h]];return t||(t={},g++,e[h]=g,v[g]=t),t}function a(e,n,r){if(n||(n=t),l)return n.createElement(e);r||(r=o(n));var a;return a=r.cache[e]?r.cache[e].cloneNode():p.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||m.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function i(e,n){if(e||(e=t),l)return e.createDocumentFragment();n=n||o(e);for(var a=n.frag.cloneNode(),i=0,c=r(),s=c.length;s>i;i++)a.createElement(c[i]);return a}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return y.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(y,t.frag)}function s(e){e||(e=t);var r=o(e);return!y.shivCSS||u||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||c(e,r),e}var u,l,d="3.7.0",f=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",g=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="",u="hidden"in e,l=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){u=!0,l=!0}}();var y={elements:f.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:d,shivCSS:f.shivCSS!==!1,supportsUnknownElements:l,shivMethods:f.shivMethods!==!1,type:"default",shivDocument:s,createElement:a,createDocumentFragment:i};e.html5=y,s(t)}(this,t),p._version=m,p._prefixes=S,p._domPrefixes=T,p._cssomPrefixes=k,p.mq=z,p.hasEvent=A,p.testProp=function(e){return c([e])},p.testAllProps=u,p.testStyles=F,p.prefixed=function(e,t,n){return t?u(e,t,n):u(e,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+$.join(" "):""),p}(this,this.document); -------------------------------------------------------------------------------- /site/js/theme.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | // Shift nav in mobile when clicking the menu. 3 | $(document).on('click', "[data-toggle='wy-nav-top']", function() { 4 | $("[data-toggle='wy-nav-shift']").toggleClass("shift"); 5 | $("[data-toggle='rst-versions']").toggleClass("shift"); 6 | }); 7 | 8 | // Close menu when you click a link. 9 | $(document).on('click', ".wy-menu-vertical .current ul li a", function() { 10 | $("[data-toggle='wy-nav-shift']").removeClass("shift"); 11 | $("[data-toggle='rst-versions']").toggleClass("shift"); 12 | }); 13 | 14 | // Keyboard navigation 15 | document.addEventListener("keydown", function(e) { 16 | if ($(e.target).is(':input')) return true; 17 | var key = e.which || e.keyCode || window.event && window.event.keyCode; 18 | var page; 19 | switch (key) { 20 | case 39: // right arrow 21 | page = $('[role="navigation"] a:contains(Next):first').prop('href'); 22 | break; 23 | case 37: // left arrow 24 | page = $('[role="navigation"] a:contains(Previous):first').prop('href'); 25 | break; 26 | default: break; 27 | } 28 | if (page) window.location.href = page; 29 | }); 30 | 31 | $(document).on('click', "[data-toggle='rst-current-version']", function() { 32 | $("[data-toggle='rst-versions']").toggleClass("shift-up"); 33 | }); 34 | 35 | // Make tables responsive 36 | $("table.docutils:not(.field-list)").wrap("
"); 37 | 38 | hljs.initHighlightingOnLoad(); 39 | 40 | $('table').addClass('docutils'); 41 | }); 42 | 43 | window.SphinxRtdTheme = (function (jquery) { 44 | var stickyNav = (function () { 45 | var navBar, 46 | win, 47 | stickyNavCssClass = 'stickynav', 48 | applyStickNav = function () { 49 | if (navBar.height() <= win.height()) { 50 | navBar.addClass(stickyNavCssClass); 51 | } else { 52 | navBar.removeClass(stickyNavCssClass); 53 | } 54 | }, 55 | enable = function () { 56 | applyStickNav(); 57 | win.on('resize', applyStickNav); 58 | }, 59 | init = function () { 60 | navBar = jquery('nav.wy-nav-side:first'); 61 | win = jquery(window); 62 | }; 63 | jquery(init); 64 | return { 65 | enable : enable 66 | }; 67 | }()); 68 | return { 69 | StickyNav : stickyNav 70 | }; 71 | }($)); 72 | 73 | // The code below is a copy of @seanmadsen code posted Jan 10, 2017 on issue 803. 74 | // https://github.com/mkdocs/mkdocs/issues/803 75 | // This just incorporates the auto scroll into the theme itself without 76 | // the need for additional custom.js file. 77 | // 78 | $(function() { 79 | $.fn.isFullyWithinViewport = function(){ 80 | var viewport = {}; 81 | viewport.top = $(window).scrollTop(); 82 | viewport.bottom = viewport.top + $(window).height(); 83 | var bounds = {}; 84 | bounds.top = this.offset().top; 85 | bounds.bottom = bounds.top + this.outerHeight(); 86 | return ( ! ( 87 | (bounds.top <= viewport.top) || 88 | (bounds.bottom >= viewport.bottom) 89 | ) ); 90 | }; 91 | if( $('li.toctree-l1.current').length && !$('li.toctree-l1.current').isFullyWithinViewport() ) { 92 | $('.wy-nav-side') 93 | .scrollTop( 94 | $('li.toctree-l1.current').offset().top - 95 | $('.wy-nav-side').offset().top - 96 | 60 97 | ); 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /site/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Keras Deep Learning on Graphs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 89 | 90 |
91 | 92 | 93 | 97 | 98 | 99 |
100 |
101 |
102 |
    103 |
  • Docs »
  • 104 | 105 | 106 |
  • 107 | 108 |
  • 109 |
110 |
111 |
112 |
113 |
114 | 115 | 116 |

Search Results

117 | 118 | 122 | 123 |
124 | Searching... 125 |
126 | 127 | 128 |
129 |
130 |
131 | 132 | 133 |
134 | 135 |
136 | 137 | 138 |
139 | 140 | Built with MkDocs using a theme provided by Read the Docs. 141 |
142 | 143 |
144 |
145 | 146 |
147 | 148 |
149 | 150 |
151 | 152 | 153 | GitHub 154 | 155 | 156 | 157 | 158 |
159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /site/search/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0 3 | * Copyright (C) 2016 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& 19 | (f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= 20 | this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); 21 | if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", 22 | "fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, 23 | a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, 24 | registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); 25 | b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, 26 | q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, 27 | e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&& 34 | !Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), 35 | s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== 36 | b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); 37 | -------------------------------------------------------------------------------- /site/search/search-results-template.mustache: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /site/search/search.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: base_url + "/search/" 3 | }); 4 | 5 | require([ 6 | 'mustache.min', 7 | 'lunr.min', 8 | 'text!search-results-template.mustache', 9 | 'text!search_index.json', 10 | ], function (Mustache, lunr, results_template, data) { 11 | "use strict"; 12 | 13 | function getSearchTerm() 14 | { 15 | var sPageURL = window.location.search.substring(1); 16 | var sURLVariables = sPageURL.split('&'); 17 | for (var i = 0; i < sURLVariables.length; i++) 18 | { 19 | var sParameterName = sURLVariables[i].split('='); 20 | if (sParameterName[0] == 'q') 21 | { 22 | return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); 23 | } 24 | } 25 | } 26 | 27 | var index = lunr(function () { 28 | this.field('title', {boost: 10}); 29 | this.field('text'); 30 | this.ref('location'); 31 | }); 32 | 33 | data = JSON.parse(data); 34 | var documents = {}; 35 | 36 | for (var i=0; i < data.docs.length; i++){ 37 | var doc = data.docs[i]; 38 | doc.location = base_url + doc.location; 39 | index.add(doc); 40 | documents[doc.location] = doc; 41 | } 42 | 43 | var search = function(){ 44 | 45 | var query = document.getElementById('mkdocs-search-query').value; 46 | var search_results = document.getElementById("mkdocs-search-results"); 47 | while (search_results.firstChild) { 48 | search_results.removeChild(search_results.firstChild); 49 | } 50 | 51 | if(query === ''){ 52 | return; 53 | } 54 | 55 | var results = index.search(query); 56 | 57 | if (results.length > 0){ 58 | for (var i=0; i < results.length; i++){ 59 | var result = results[i]; 60 | doc = documents[result.ref]; 61 | doc.base_url = base_url; 62 | doc.summary = doc.text.substring(0, 200); 63 | var html = Mustache.to_html(results_template, doc); 64 | search_results.insertAdjacentHTML('beforeend', html); 65 | } 66 | } else { 67 | search_results.insertAdjacentHTML('beforeend', "

No results found

"); 68 | } 69 | 70 | if(jQuery){ 71 | /* 72 | * We currently only automatically hide bootstrap models. This 73 | * requires jQuery to work. 74 | */ 75 | jQuery('#mkdocs_search_modal a').click(function(){ 76 | jQuery('#mkdocs_search_modal').modal('hide'); 77 | }); 78 | } 79 | 80 | }; 81 | 82 | var search_input = document.getElementById('mkdocs-search-query'); 83 | 84 | var term = getSearchTerm(); 85 | if (term){ 86 | search_input.value = term; 87 | search(); 88 | } 89 | 90 | if (search_input){search_input.addEventListener("keyup", search);} 91 | 92 | }); 93 | -------------------------------------------------------------------------------- /site/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/ 7 | 2018-05-23 8 | daily 9 | 10 | 11 | 12 | 13 | 14 | 15 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Layers/Convolution/graph_conv_layer/ 16 | 2018-05-23 17 | daily 18 | 19 | 20 | 21 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Layers/Attention/graph_attention_layer/ 22 | 2018-05-23 23 | daily 24 | 25 | 26 | 27 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Layers/Recurrent/graph_conv_recurrent_layer/ 28 | 2018-05-23 29 | daily 30 | 31 | 32 | 33 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Layers/Graph Capsule Neural Network/graph_capsule_cnn/ 34 | 2018-05-23 35 | daily 36 | 37 | 38 | 39 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Layers/Graph Neural Network/graph_neural_networks/ 40 | 2018-05-23 41 | daily 42 | 43 | 44 | 45 | 46 | 47 | 48 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/Filters/graph_conv_filters/ 49 | 2018-05-23 50 | daily 51 | 52 | 53 | 54 | 55 | 56 | http://vermaMachineLearning.github.io/keras-deep-graph-learning/about/ 57 | 2018-05-23 58 | daily 59 | 60 | 61 | 62 | --------------------------------------------------------------------------------