We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
A model doesn't know its input_shape after build
Reproduce the error as

``` python
model = Sequential()
model.add(Dense(1), input_shape=(784,))
model.build()

model.input_shape
```

Shouldn't the model know its `input_shape` after `build`? It knows `output_shape` for instance. Am I missing anything @matsuyamax ?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `keras/layers/containers.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import
3 from __future__ import print_function
4 
5 from collections import OrderedDict
6 import theano.tensor as T
7 from ..layers.core import Layer, Merge
8 from ..utils.theano_utils import ndim_tensor
9 from six.moves import range
10 
11 
12 class Sequential(Layer):
13     '''
14         Simple linear stack of layers.
15 
16         inherited from Layer:
17         - get_params
18         - get_output_mask
19         - supports_masked_input
20     '''
21 
22     def __init__(self, layers=[]):
23         self.layers = []
24         for layer in layers:
25             self.add(layer)
26 
27     def set_previous(self, layer):
28         self.layers[0].previous = layer
29 
30     def add(self, layer):
31         self.layers.append(layer)
32         if len(self.layers) > 1:
33             self.layers[-1].set_previous(self.layers[-2])
34             if not hasattr(self.layers[0], 'input'):
35                 self.set_input()
36 
37     @property
38     def params(self):
39         params = []
40         for l in self.layers:
41             if l.trainable:
42                 params += l.get_params()[0]
43         return params
44 
45     @property
46     def regularizers(self):
47         regularizers = []
48         for l in self.layers:
49             if l.trainable:
50                 regularizers += l.get_params()[1]
51         return regularizers
52 
53     @property
54     def constraints(self):
55         constraints = []
56         for l in self.layers:
57             if l.trainable:
58                 constraints += l.get_params()[2]
59         return constraints
60 
61     @property
62     def updates(self):
63         updates = []
64         for l in self.layers:
65             if l.trainable:
66                 updates += l.get_params()[3]
67         return updates
68 
69     @property
70     def output_shape(self):
71         return self.layers[-1].output_shape
72 
73     def get_output(self, train=False):
74         return self.layers[-1].get_output(train)
75 
76     def set_input(self):
77         for l in self.layers:
78             if hasattr(l, 'input'):
79                 ndim = l.input.ndim
80                 self.layers[0].input = ndim_tensor(ndim)
81                 break
82 
83     def get_input(self, train=False):
84         if not hasattr(self.layers[0], 'input'):
85             self.set_input()
86         return self.layers[0].get_input(train)
87 
88     @property
89     def input(self):
90         return self.get_input()
91 
92     def get_weights(self):
93         weights = []
94         for layer in self.layers:
95             weights += layer.get_weights()
96         return weights
97 
98     def set_weights(self, weights):
99         for i in range(len(self.layers)):
100             nb_param = len(self.layers[i].params)
101             self.layers[i].set_weights(weights[:nb_param])
102             weights = weights[nb_param:]
103 
104     def get_config(self):
105         return {"name": self.__class__.__name__,
106                 "layers": [layer.get_config() for layer in self.layers]}
107 
108     def count_params(self):
109         return sum([layer.count_params() for layer in self.layers])
110 
111 
112 class Graph(Layer):
113     '''
114         Implement a NN graph with arbitrary layer connections,
115         arbitrary number of inputs and arbitrary number of outputs.
116 
117         Note: Graph can only be used as a layer
118         (connect, input, get_input, get_output)
119         when it has exactly one input and one output.
120 
121         inherited from Layer:
122             - get_output_mask
123             - supports_masked_input
124             - get_weights
125             - set_weights
126     '''
127     def __init__(self):
128         self.namespace = set()  # strings
129         self.nodes = OrderedDict()  # layer-like
130         self.inputs = {}  # layer-like
131         self.input_order = []  # strings
132         self.outputs = {}  # layer-like
133         self.output_order = []  # strings
134         self.input_config = []  # dicts
135         self.output_config = []  # dicts
136         self.node_config = []  # dicts
137 
138     @property
139     def nb_input(self):
140         return len(self.inputs)
141 
142     @property
143     def nb_output(self):
144         return len(self.outputs)
145 
146     @property
147     def params(self):
148         params = []
149         for l in self.nodes.values():
150             if l.trainable:
151                 params += l.get_params()[0]
152         return params
153 
154     @property
155     def regularizers(self):
156         regularizers = []
157         for l in self.nodes.values():
158             if l.trainable:
159                 regularizers += l.get_params()[1]
160         return regularizers
161 
162     @property
163     def constraints(self):
164         constraints = []
165         for l in self.nodes.values():
166             if l.trainable:
167                 constraints += l.get_params()[2]
168         return constraints
169 
170     @property
171     def updates(self):
172         updates = []
173         for l in self.nodes.values():
174             if l.trainable:
175                 updates += l.get_params()[3]
176         return updates
177 
178     def set_previous(self, layer, connection_map={}):
179         if self.nb_input != layer.nb_output:
180             raise Exception('Cannot connect layers: input count does not match output count.')
181         if self.nb_input == 1:
182             self.inputs[self.input_order[0]].set_previous(layer)
183         else:
184             if not connection_map:
185                 raise Exception('Cannot attach multi-input layer: no connection_map provided.')
186             for k, v in connection_map.items():
187                 if k in self.inputs and v in layer.outputs:
188                     self.inputs[k].set_previous(layer.outputs[v])
189                 else:
190                     raise Exception('Invalid connection map.')
191 
192     def get_input(self, train=False):
193         if len(self.inputs) == len(self.outputs) == 1:
194             return self.inputs[self.input_order[0]].get_input(train)
195         else:
196             return dict([(k, v.get_input(train)) for k, v in self.inputs.items()])
197 
198     @property
199     def input(self):
200         return self.get_input()
201 
202     @property
203     def output_shape(self):
204         if self.nb_output == 1:
205             # return tuple
206             return self.outputs[self.output_order[0]].output_shape
207         else:
208             # return dictionary mapping output names to shape tuples
209             return dict([(k, v.output_shape) for k, v in self.outputs.items()])
210 
211     def get_output(self, train=False):
212         if len(self.inputs) == len(self.outputs) == 1:
213             return self.outputs[self.output_order[0]].get_output(train)
214         else:
215             return dict([(k, v.get_output(train)) for k, v in self.outputs.items()])
216 
217     def add_input(self, name, input_shape, dtype='float'):
218         if name in self.namespace:
219             raise Exception('Duplicate node identifier: ' + name)
220         self.namespace.add(name)
221         self.input_order.append(name)
222         layer = Layer()  # empty layer
223         layer.set_input_shape(input_shape)
224         ndim = len(input_shape) + 1
225         if dtype == 'float':
226             layer.input = ndim_tensor(ndim)
227         else:
228             if ndim == 2:
229                 layer.input = T.imatrix()
230             else:
231                 raise Exception('Type "int" can only be used with ndim==2 (Embedding).')
232         layer.input.name = name
233         self.inputs[name] = layer
234         self.input_config.append({'name': name,
235                                   'input_shape': input_shape,
236                                   'dtype': dtype})
237 
238     def add_node(self, layer, name, input=None, inputs=[],
239                  merge_mode='concat', concat_axis=-1, create_output=False):
240         if hasattr(layer, 'set_name'):
241             layer.set_name(name)
242         if name in self.namespace:
243             raise Exception('Duplicate node identifier: ' + name)
244         if input:
245             if input not in self.namespace:
246                 raise Exception('Unknown node/input identifier: ' + input)
247             if input in self.nodes:
248                 layer.set_previous(self.nodes[input])
249             elif input in self.inputs:
250                 layer.set_previous(self.inputs[input])
251         if inputs:
252             to_merge = []
253             for n in inputs:
254                 if n in self.nodes:
255                     to_merge.append(self.nodes[n])
256                 elif n in self.inputs:
257                     to_merge.append(self.inputs[n])
258                 else:
259                     raise Exception('Unknown identifier: ' + n)
260             merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)
261             layer.set_previous(merge)
262 
263         self.namespace.add(name)
264         self.nodes[name] = layer
265         self.node_config.append({'name': name,
266                                  'input': input,
267                                  'inputs': inputs,
268                                  'merge_mode': merge_mode,
269                                  'concat_axis': concat_axis,
270                                  'create_output': create_output})
271 
272         if create_output:
273             self.add_output(name, input=name)
274 
275     def add_output(self, name, input=None, inputs=[],
276                    merge_mode='concat', concat_axis=-1):
277         if name in self.output_order:
278             raise Exception('Duplicate output identifier: ' + name)
279         if input:
280             if input not in self.namespace:
281                 raise Exception('Unknown node/input identifier: ' + input)
282             if input in self.nodes:
283                 self.outputs[name] = self.nodes[input]
284             elif input in self.inputs:
285                 self.outputs[name] = self.inputs[input]
286         if inputs:
287             to_merge = []
288             for n in inputs:
289                 if n not in self.nodes:
290                     raise Exception('Unknown identifier: ' + n)
291                 to_merge.append(self.nodes[n])
292             merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)
293             self.outputs[name] = merge
294 
295         self.output_order.append(name)
296         self.output_config.append({'name': name,
297                                    'input': input,
298                                    'inputs': inputs,
299                                    'merge_mode': merge_mode,
300                                    'concat_axis': concat_axis})
301 
302     def get_config(self):
303         return {"name": self.__class__.__name__,
304                 "input_config": self.input_config,
305                 "node_config": self.node_config,
306                 "output_config": self.output_config,
307                 "input_order": self.input_order,
308                 "output_order": self.output_order,
309                 "nodes": dict([(c["name"], self.nodes[c["name"]].get_config()) for c in self.node_config])}
310 
311     def count_params(self):
312         return sum([layer.count_params() for layer in self.nodes.values()])
313
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/keras/layers/containers.py b/keras/layers/containers.py
--- a/keras/layers/containers.py
+++ b/keras/layers/containers.py
@@ -84,7 +84,11 @@
         if not hasattr(self.layers[0], 'input'):
             self.set_input()
         return self.layers[0].get_input(train)
-
+ 
+    @property
+    def input_shape(self):
+        return self.layers[0].input_shape
+    
     @property
     def input(self):
         return self.get_input()

{"golden_diff": "diff --git a/keras/layers/containers.py b/keras/layers/containers.py\n--- a/keras/layers/containers.py\n+++ b/keras/layers/containers.py\n@@ -84,7 +84,11 @@\n         if not hasattr(self.layers[0], 'input'):\n             self.set_input()\n         return self.layers[0].get_input(train)\n-\n+ \n+    @property\n+    def input_shape(self):\n+        return self.layers[0].input_shape\n+    \n     @property\n     def input(self):\n         return self.get_input()\n", "issue": "A model doesn't know its input_shape after build\nReproduce the error as\n\n``` python\nmodel = Sequential()\nmodel.add(Dense(1), input_shape=(784,))\nmodel.build()\n\nmodel.input_shape\n```\n\nShouldn't the model know its `input_shape` after `build`? It knows `output_shape` for instance. Am I missing anything @matsuyamax ?\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import\nfrom __future__ import print_function\n\nfrom collections import OrderedDict\nimport theano.tensor as T\nfrom ..layers.core import Layer, Merge\nfrom ..utils.theano_utils import ndim_tensor\nfrom six.moves import range\n\n\nclass Sequential(Layer):\n    '''\n        Simple linear stack of layers.\n\n        inherited from Layer:\n        - get_params\n        - get_output_mask\n        - supports_masked_input\n    '''\n\n    def __init__(self, layers=[]):\n        self.layers = []\n        for layer in layers:\n            self.add(layer)\n\n    def set_previous(self, layer):\n        self.layers[0].previous = layer\n\n    def add(self, layer):\n        self.layers.append(layer)\n        if len(self.layers) > 1:\n            self.layers[-1].set_previous(self.layers[-2])\n            if not hasattr(self.layers[0], 'input'):\n                self.set_input()\n\n    @property\n    def params(self):\n        params = []\n        for l in self.layers:\n            if l.trainable:\n                params += l.get_params()[0]\n        return params\n\n    @property\n    def regularizers(self):\n        regularizers = []\n        for l in self.layers:\n            if l.trainable:\n                regularizers += l.get_params()[1]\n        return regularizers\n\n    @property\n    def constraints(self):\n        constraints = []\n        for l in self.layers:\n            if l.trainable:\n                constraints += l.get_params()[2]\n        return constraints\n\n    @property\n    def updates(self):\n        updates = []\n        for l in self.layers:\n            if l.trainable:\n                updates += l.get_params()[3]\n        return updates\n\n    @property\n    def output_shape(self):\n        return self.layers[-1].output_shape\n\n    def get_output(self, train=False):\n        return self.layers[-1].get_output(train)\n\n    def set_input(self):\n        for l in self.layers:\n            if hasattr(l, 'input'):\n                ndim = l.input.ndim\n                self.layers[0].input = ndim_tensor(ndim)\n                break\n\n    def get_input(self, train=False):\n        if not hasattr(self.layers[0], 'input'):\n            self.set_input()\n        return self.layers[0].get_input(train)\n\n    @property\n    def input(self):\n        return self.get_input()\n\n    def get_weights(self):\n        weights = []\n        for layer in self.layers:\n            weights += layer.get_weights()\n        return weights\n\n    def set_weights(self, weights):\n        for i in range(len(self.layers)):\n            nb_param = len(self.layers[i].params)\n            self.layers[i].set_weights(weights[:nb_param])\n            weights = weights[nb_param:]\n\n    def get_config(self):\n        return {\"name\": self.__class__.__name__,\n                \"layers\": [layer.get_config() for layer in self.layers]}\n\n    def count_params(self):\n        return sum([layer.count_params() for layer in self.layers])\n\n\nclass Graph(Layer):\n    '''\n        Implement a NN graph with arbitrary layer connections,\n        arbitrary number of inputs and arbitrary number of outputs.\n\n        Note: Graph can only be used as a layer\n        (connect, input, get_input, get_output)\n        when it has exactly one input and one output.\n\n        inherited from Layer:\n            - get_output_mask\n            - supports_masked_input\n            - get_weights\n            - set_weights\n    '''\n    def __init__(self):\n        self.namespace = set()  # strings\n        self.nodes = OrderedDict()  # layer-like\n        self.inputs = {}  # layer-like\n        self.input_order = []  # strings\n        self.outputs = {}  # layer-like\n        self.output_order = []  # strings\n        self.input_config = []  # dicts\n        self.output_config = []  # dicts\n        self.node_config = []  # dicts\n\n    @property\n    def nb_input(self):\n        return len(self.inputs)\n\n    @property\n    def nb_output(self):\n        return len(self.outputs)\n\n    @property\n    def params(self):\n        params = []\n        for l in self.nodes.values():\n            if l.trainable:\n                params += l.get_params()[0]\n        return params\n\n    @property\n    def regularizers(self):\n        regularizers = []\n        for l in self.nodes.values():\n            if l.trainable:\n                regularizers += l.get_params()[1]\n        return regularizers\n\n    @property\n    def constraints(self):\n        constraints = []\n        for l in self.nodes.values():\n            if l.trainable:\n                constraints += l.get_params()[2]\n        return constraints\n\n    @property\n    def updates(self):\n        updates = []\n        for l in self.nodes.values():\n            if l.trainable:\n                updates += l.get_params()[3]\n        return updates\n\n    def set_previous(self, layer, connection_map={}):\n        if self.nb_input != layer.nb_output:\n            raise Exception('Cannot connect layers: input count does not match output count.')\n        if self.nb_input == 1:\n            self.inputs[self.input_order[0]].set_previous(layer)\n        else:\n            if not connection_map:\n                raise Exception('Cannot attach multi-input layer: no connection_map provided.')\n            for k, v in connection_map.items():\n                if k in self.inputs and v in layer.outputs:\n                    self.inputs[k].set_previous(layer.outputs[v])\n                else:\n                    raise Exception('Invalid connection map.')\n\n    def get_input(self, train=False):\n        if len(self.inputs) == len(self.outputs) == 1:\n            return self.inputs[self.input_order[0]].get_input(train)\n        else:\n            return dict([(k, v.get_input(train)) for k, v in self.inputs.items()])\n\n    @property\n    def input(self):\n        return self.get_input()\n\n    @property\n    def output_shape(self):\n        if self.nb_output == 1:\n            # return tuple\n            return self.outputs[self.output_order[0]].output_shape\n        else:\n            # return dictionary mapping output names to shape tuples\n            return dict([(k, v.output_shape) for k, v in self.outputs.items()])\n\n    def get_output(self, train=False):\n        if len(self.inputs) == len(self.outputs) == 1:\n            return self.outputs[self.output_order[0]].get_output(train)\n        else:\n            return dict([(k, v.get_output(train)) for k, v in self.outputs.items()])\n\n    def add_input(self, name, input_shape, dtype='float'):\n        if name in self.namespace:\n            raise Exception('Duplicate node identifier: ' + name)\n        self.namespace.add(name)\n        self.input_order.append(name)\n        layer = Layer()  # empty layer\n        layer.set_input_shape(input_shape)\n        ndim = len(input_shape) + 1\n        if dtype == 'float':\n            layer.input = ndim_tensor(ndim)\n        else:\n            if ndim == 2:\n                layer.input = T.imatrix()\n            else:\n                raise Exception('Type \"int\" can only be used with ndim==2 (Embedding).')\n        layer.input.name = name\n        self.inputs[name] = layer\n        self.input_config.append({'name': name,\n                                  'input_shape': input_shape,\n                                  'dtype': dtype})\n\n    def add_node(self, layer, name, input=None, inputs=[],\n                 merge_mode='concat', concat_axis=-1, create_output=False):\n        if hasattr(layer, 'set_name'):\n            layer.set_name(name)\n        if name in self.namespace:\n            raise Exception('Duplicate node identifier: ' + name)\n        if input:\n            if input not in self.namespace:\n                raise Exception('Unknown node/input identifier: ' + input)\n            if input in self.nodes:\n                layer.set_previous(self.nodes[input])\n            elif input in self.inputs:\n                layer.set_previous(self.inputs[input])\n        if inputs:\n            to_merge = []\n            for n in inputs:\n                if n in self.nodes:\n                    to_merge.append(self.nodes[n])\n                elif n in self.inputs:\n                    to_merge.append(self.inputs[n])\n                else:\n                    raise Exception('Unknown identifier: ' + n)\n            merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)\n            layer.set_previous(merge)\n\n        self.namespace.add(name)\n        self.nodes[name] = layer\n        self.node_config.append({'name': name,\n                                 'input': input,\n                                 'inputs': inputs,\n                                 'merge_mode': merge_mode,\n                                 'concat_axis': concat_axis,\n                                 'create_output': create_output})\n\n        if create_output:\n            self.add_output(name, input=name)\n\n    def add_output(self, name, input=None, inputs=[],\n                   merge_mode='concat', concat_axis=-1):\n        if name in self.output_order:\n            raise Exception('Duplicate output identifier: ' + name)\n        if input:\n            if input not in self.namespace:\n                raise Exception('Unknown node/input identifier: ' + input)\n            if input in self.nodes:\n                self.outputs[name] = self.nodes[input]\n            elif input in self.inputs:\n                self.outputs[name] = self.inputs[input]\n        if inputs:\n            to_merge = []\n            for n in inputs:\n                if n not in self.nodes:\n                    raise Exception('Unknown identifier: ' + n)\n                to_merge.append(self.nodes[n])\n            merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)\n            self.outputs[name] = merge\n\n        self.output_order.append(name)\n        self.output_config.append({'name': name,\n                                   'input': input,\n                                   'inputs': inputs,\n                                   'merge_mode': merge_mode,\n                                   'concat_axis': concat_axis})\n\n    def get_config(self):\n        return {\"name\": self.__class__.__name__,\n                \"input_config\": self.input_config,\n                \"node_config\": self.node_config,\n                \"output_config\": self.output_config,\n                \"input_order\": self.input_order,\n                \"output_order\": self.output_order,\n                \"nodes\": dict([(c[\"name\"], self.nodes[c[\"name\"]].get_config()) for c in self.node_config])}\n\n    def count_params(self):\n        return sum([layer.count_params() for layer in self.nodes.values()])\n", "path": "keras/layers/containers.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import\nfrom __future__ import print_function\n\nfrom collections import OrderedDict\nimport theano.tensor as T\nfrom ..layers.core import Layer, Merge\nfrom ..utils.theano_utils import ndim_tensor\nfrom six.moves import range\n\n\nclass Sequential(Layer):\n    '''\n        Simple linear stack of layers.\n\n        inherited from Layer:\n        - get_params\n        - get_output_mask\n        - supports_masked_input\n    '''\n\n    def __init__(self, layers=[]):\n        self.layers = []\n        for layer in layers:\n            self.add(layer)\n\n    def set_previous(self, layer):\n        self.layers[0].previous = layer\n\n    def add(self, layer):\n        self.layers.append(layer)\n        if len(self.layers) > 1:\n            self.layers[-1].set_previous(self.layers[-2])\n            if not hasattr(self.layers[0], 'input'):\n                self.set_input()\n\n    @property\n    def params(self):\n        params = []\n        for l in self.layers:\n            if l.trainable:\n                params += l.get_params()[0]\n        return params\n\n    @property\n    def regularizers(self):\n        regularizers = []\n        for l in self.layers:\n            if l.trainable:\n                regularizers += l.get_params()[1]\n        return regularizers\n\n    @property\n    def constraints(self):\n        constraints = []\n        for l in self.layers:\n            if l.trainable:\n                constraints += l.get_params()[2]\n        return constraints\n\n    @property\n    def updates(self):\n        updates = []\n        for l in self.layers:\n            if l.trainable:\n                updates += l.get_params()[3]\n        return updates\n\n    @property\n    def output_shape(self):\n        return self.layers[-1].output_shape\n\n    def get_output(self, train=False):\n        return self.layers[-1].get_output(train)\n\n    def set_input(self):\n        for l in self.layers:\n            if hasattr(l, 'input'):\n                ndim = l.input.ndim\n                self.layers[0].input = ndim_tensor(ndim)\n                break\n\n    def get_input(self, train=False):\n        if not hasattr(self.layers[0], 'input'):\n            self.set_input()\n        return self.layers[0].get_input(train)\n \n    @property\n    def input_shape(self):\n        return self.layers[0].input_shape\n    \n    @property\n    def input(self):\n        return self.get_input()\n\n    def get_weights(self):\n        weights = []\n        for layer in self.layers:\n            weights += layer.get_weights()\n        return weights\n\n    def set_weights(self, weights):\n        for i in range(len(self.layers)):\n            nb_param = len(self.layers[i].params)\n            self.layers[i].set_weights(weights[:nb_param])\n            weights = weights[nb_param:]\n\n    def get_config(self):\n        return {\"name\": self.__class__.__name__,\n                \"layers\": [layer.get_config() for layer in self.layers]}\n\n    def count_params(self):\n        return sum([layer.count_params() for layer in self.layers])\n\n\nclass Graph(Layer):\n    '''\n        Implement a NN graph with arbitrary layer connections,\n        arbitrary number of inputs and arbitrary number of outputs.\n\n        Note: Graph can only be used as a layer\n        (connect, input, get_input, get_output)\n        when it has exactly one input and one output.\n\n        inherited from Layer:\n            - get_output_mask\n            - supports_masked_input\n            - get_weights\n            - set_weights\n    '''\n    def __init__(self):\n        self.namespace = set()  # strings\n        self.nodes = OrderedDict()  # layer-like\n        self.inputs = {}  # layer-like\n        self.input_order = []  # strings\n        self.outputs = {}  # layer-like\n        self.output_order = []  # strings\n        self.input_config = []  # dicts\n        self.output_config = []  # dicts\n        self.node_config = []  # dicts\n\n    @property\n    def nb_input(self):\n        return len(self.inputs)\n\n    @property\n    def nb_output(self):\n        return len(self.outputs)\n\n    @property\n    def params(self):\n        params = []\n        for l in self.nodes.values():\n            if l.trainable:\n                params += l.get_params()[0]\n        return params\n\n    @property\n    def regularizers(self):\n        regularizers = []\n        for l in self.nodes.values():\n            if l.trainable:\n                regularizers += l.get_params()[1]\n        return regularizers\n\n    @property\n    def constraints(self):\n        constraints = []\n        for l in self.nodes.values():\n            if l.trainable:\n                constraints += l.get_params()[2]\n        return constraints\n\n    @property\n    def updates(self):\n        updates = []\n        for l in self.nodes.values():\n            if l.trainable:\n                updates += l.get_params()[3]\n        return updates\n\n    def set_previous(self, layer, connection_map={}):\n        if self.nb_input != layer.nb_output:\n            raise Exception('Cannot connect layers: input count does not match output count.')\n        if self.nb_input == 1:\n            self.inputs[self.input_order[0]].set_previous(layer)\n        else:\n            if not connection_map:\n                raise Exception('Cannot attach multi-input layer: no connection_map provided.')\n            for k, v in connection_map.items():\n                if k in self.inputs and v in layer.outputs:\n                    self.inputs[k].set_previous(layer.outputs[v])\n                else:\n                    raise Exception('Invalid connection map.')\n\n    def get_input(self, train=False):\n        if len(self.inputs) == len(self.outputs) == 1:\n            return self.inputs[self.input_order[0]].get_input(train)\n        else:\n            return dict([(k, v.get_input(train)) for k, v in self.inputs.items()])\n\n    @property\n    def input(self):\n        return self.get_input()\n\n    @property\n    def output_shape(self):\n        if self.nb_output == 1:\n            # return tuple\n            return self.outputs[self.output_order[0]].output_shape\n        else:\n            # return dictionary mapping output names to shape tuples\n            return dict([(k, v.output_shape) for k, v in self.outputs.items()])\n\n    def get_output(self, train=False):\n        if len(self.inputs) == len(self.outputs) == 1:\n            return self.outputs[self.output_order[0]].get_output(train)\n        else:\n            return dict([(k, v.get_output(train)) for k, v in self.outputs.items()])\n\n    def add_input(self, name, input_shape, dtype='float'):\n        if name in self.namespace:\n            raise Exception('Duplicate node identifier: ' + name)\n        self.namespace.add(name)\n        self.input_order.append(name)\n        layer = Layer()  # empty layer\n        layer.set_input_shape(input_shape)\n        ndim = len(input_shape) + 1\n        if dtype == 'float':\n            layer.input = ndim_tensor(ndim)\n        else:\n            if ndim == 2:\n                layer.input = T.imatrix()\n            else:\n                raise Exception('Type \"int\" can only be used with ndim==2 (Embedding).')\n        layer.input.name = name\n        self.inputs[name] = layer\n        self.input_config.append({'name': name,\n                                  'input_shape': input_shape,\n                                  'dtype': dtype})\n\n    def add_node(self, layer, name, input=None, inputs=[],\n                 merge_mode='concat', concat_axis=-1, create_output=False):\n        if hasattr(layer, 'set_name'):\n            layer.set_name(name)\n        if name in self.namespace:\n            raise Exception('Duplicate node identifier: ' + name)\n        if input:\n            if input not in self.namespace:\n                raise Exception('Unknown node/input identifier: ' + input)\n            if input in self.nodes:\n                layer.set_previous(self.nodes[input])\n            elif input in self.inputs:\n                layer.set_previous(self.inputs[input])\n        if inputs:\n            to_merge = []\n            for n in inputs:\n                if n in self.nodes:\n                    to_merge.append(self.nodes[n])\n                elif n in self.inputs:\n                    to_merge.append(self.inputs[n])\n                else:\n                    raise Exception('Unknown identifier: ' + n)\n            merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)\n            layer.set_previous(merge)\n\n        self.namespace.add(name)\n        self.nodes[name] = layer\n        self.node_config.append({'name': name,\n                                 'input': input,\n                                 'inputs': inputs,\n                                 'merge_mode': merge_mode,\n                                 'concat_axis': concat_axis,\n                                 'create_output': create_output})\n\n        if create_output:\n            self.add_output(name, input=name)\n\n    def add_output(self, name, input=None, inputs=[],\n                   merge_mode='concat', concat_axis=-1):\n        if name in self.output_order:\n            raise Exception('Duplicate output identifier: ' + name)\n        if input:\n            if input not in self.namespace:\n                raise Exception('Unknown node/input identifier: ' + input)\n            if input in self.nodes:\n                self.outputs[name] = self.nodes[input]\n            elif input in self.inputs:\n                self.outputs[name] = self.inputs[input]\n        if inputs:\n            to_merge = []\n            for n in inputs:\n                if n not in self.nodes:\n                    raise Exception('Unknown identifier: ' + n)\n                to_merge.append(self.nodes[n])\n            merge = Merge(to_merge, mode=merge_mode, concat_axis=concat_axis)\n            self.outputs[name] = merge\n\n        self.output_order.append(name)\n        self.output_config.append({'name': name,\n                                   'input': input,\n                                   'inputs': inputs,\n                                   'merge_mode': merge_mode,\n                                   'concat_axis': concat_axis})\n\n    def get_config(self):\n        return {\"name\": self.__class__.__name__,\n                \"input_config\": self.input_config,\n                \"node_config\": self.node_config,\n                \"output_config\": self.output_config,\n                \"input_order\": self.input_order,\n                \"output_order\": self.output_order,\n                \"nodes\": dict([(c[\"name\"], self.nodes[c[\"name\"]].get_config()) for c in self.node_config])}\n\n    def count_params(self):\n        return sum([layer.count_params() for layer in self.nodes.values()])\n", "path": "keras/layers/containers.py"}]}