44using System . Text ;
55using Tensorflow . Keras . ArgsDefinition ;
66using Tensorflow . Keras . Utils ;
7+ using static Tensorflow . Binding ;
78
89namespace Tensorflow . Keras . Engine
910{
@@ -21,6 +22,11 @@ public class Functional : Model
2122 List < Layer > _input_layers ;
2223 List < KerasHistory > _input_coordinates ;
2324 List < KerasHistory > _output_coordinates ;
25+ public string [ ] NetworkNodes { get ; set ; }
26+ public Dictionary < int , List < Node > > NodesByDepth { get ; set ; }
27+ public List < Layer > Layers { get ; set ; }
28+ Dictionary < int , int > tensor_usage_count ;
29+ public Dictionary < int , int > TensorUsageCount => tensor_usage_count ;
2430
2531 public Functional ( Tensors inputs , Tensors outputs )
2632 : base ( new ModelArgs
@@ -33,6 +39,7 @@ public Functional(Tensors inputs, Tensors outputs)
3339 _output_layers = new List < Layer > ( ) ;
3440 _input_coordinates = new List < KerasHistory > ( ) ;
3541 _output_coordinates = new List < KerasHistory > ( ) ;
42+ tensor_usage_count = new Dictionary < int , int > ( ) ;
3643 _init_graph_network ( inputs , outputs ) ;
3744 }
3845
@@ -67,16 +74,253 @@ void _init_graph_network(Tensors inputs, Tensors outputs)
6774 _input_layers . append ( layer ) ;
6875 _input_coordinates . append ( new KerasHistory ( layer , node_index , tensor_index , x ) ) ;
6976 }
77+
78+ // Keep track of the network's nodes and layers.
79+ var ( nodes , nodes_by_depth , layers , _) = MapGraphNetwork ( inputs , outputs ) ;
80+
81+ NetworkNodes = nodes ;
82+ NodesByDepth = nodes_by_depth ;
83+ Layers = layers ;
84+
85+ ComputeTensorUsageCount ( ) ;
7086 }
7187
72- protected override Tensors call_fn ( Tensors inputs , Tensor state = null , bool is_training = false )
88+ void ComputeTensorUsageCount ( )
7389 {
74- return run_internal_graph ( inputs , state , is_training ) ;
90+ var available_tensors = inputs . Select ( x => x . GetHashCode ( ) ) . ToList ( ) ;
91+ var depth_keys = NodesByDepth . Keys . Reverse ( ) . Skip ( 1 ) . ToArray ( ) ;
92+ foreach ( var depth in depth_keys )
93+ {
94+ foreach ( var node in NodesByDepth [ depth ] )
95+ {
96+ var input_tensors = node . KerasInputs . Select ( x => x . GetHashCode ( ) ) . ToArray ( ) ;
97+ if ( input_tensors . issubset ( available_tensors ) )
98+ {
99+ foreach ( var tensor in node . KerasInputs )
100+ {
101+ if ( ! tensor_usage_count . ContainsKey ( tensor . GetHashCode ( ) ) )
102+ tensor_usage_count [ tensor . GetHashCode ( ) ] = 0 ;
103+ tensor_usage_count [ tensor . GetHashCode ( ) ] += 1 ;
104+ }
105+
106+ foreach ( var output_tensor in node . Outputs )
107+ available_tensors . Add ( output_tensor . GetHashCode ( ) ) ;
108+ }
109+ }
110+ }
111+
112+ foreach ( var tensor in outputs )
113+ {
114+ if ( ! tensor_usage_count . ContainsKey ( tensor . GetHashCode ( ) ) )
115+ tensor_usage_count [ tensor . GetHashCode ( ) ] = 0 ;
116+ tensor_usage_count [ tensor . GetHashCode ( ) ] += 1 ;
117+ }
118+ }
119+
120+ /// <summary>
121+ /// Validates a network's topology and gather its layers and nodes.
122+ /// </summary>
123+ /// <param name="inputs"></param>
124+ /// <param name="outputs"></param>
125+ ( string [ ] , Dictionary < int , List < Node > > , List < Layer > , Dictionary < int , List < Layer > > ) MapGraphNetwork ( Tensors inputs , Tensors outputs )
126+ {
127+ var ( nodes_in_decreasing_depth , layer_indices ) = BuildMap ( outputs ) ;
128+ var network_nodes = nodes_in_decreasing_depth
129+ . Select ( node => MakeNodeKey ( node . Layer . Name , node . Layer . InboundNodes . IndexOf ( node ) ) )
130+ . ToArray ( ) ;
131+
132+ var nodes_depths = new Dictionary < Node , int > ( ) ;
133+ var layers_depths = new Dictionary < Layer , int > ( ) ;
134+
135+ nodes_in_decreasing_depth . Reverse ( ) ;
136+ foreach ( var node in nodes_in_decreasing_depth )
137+ {
138+ // If the depth is not set, the node has no outbound nodes (depth 0).
139+ int depth = nodes_depths . SetDefault ( node , 0 ) ;
140+ // Update the depth of the corresponding layer
141+ int previous_depth = layers_depths . Get ( node . Layer , 0 ) ;
142+ // If we've seen this layer before at a higher depth,
143+ // we should use that depth instead of the node depth.
144+ // This is necessary for shared layers that have inputs at different
145+ // depth levels in the graph.
146+ depth = Math . Max ( depth , previous_depth ) ;
147+ layers_depths [ node . Layer ] = depth ;
148+ nodes_depths [ node ] = depth ;
149+
150+ // Update the depth of inbound nodes.
151+ // The "depth" of a node is the max of the depths
152+ // of all nodes it is connected to + 1.
153+ foreach ( var node_dep in node . ParentNodes )
154+ {
155+ previous_depth = nodes_depths . Get ( node_dep , 0 ) ;
156+ nodes_depths [ node_dep ] = Math . Max ( depth + 1 , previous_depth ) ;
157+ }
158+ }
159+
160+ // Handle inputs that are not connected to outputs.
161+ // We do not error out here because the inputs may be used to compute losses
162+ // and metrics.
163+ foreach ( var input_t in inputs )
164+ {
165+ var ( input_layer , _, _) = input_t . KerasHistory ;
166+ if ( ! layers_depths . ContainsKey ( input_layer ) )
167+ {
168+ layers_depths [ input_layer ] = 0 ;
169+ layer_indices [ input_layer ] = - 1 ;
170+ nodes_depths [ input_layer . InboundNodes [ 0 ] ] = 0 ;
171+ network_nodes . add ( MakeNodeKey ( input_layer . Name , 0 ) ) ;
172+ }
173+ }
174+
175+ // Build a dict {depth: list of nodes with this depth}
176+ var nodes_by_depth = new Dictionary < int , List < Node > > ( ) ;
177+ foreach ( var node in nodes_depths )
178+ {
179+ if ( ! nodes_by_depth . ContainsKey ( node . Value ) )
180+ nodes_by_depth [ node . Value ] = new List < Node > ( ) ;
181+ nodes_by_depth [ node . Value ] . append ( node . Key ) ;
182+ }
183+
184+ var layers_by_depth = new Dictionary < int , List < Layer > > ( ) ;
185+ foreach ( var layer in layers_depths )
186+ {
187+ if ( ! layers_by_depth . ContainsKey ( layer . Value ) )
188+ layers_by_depth [ layer . Value ] = new List < Layer > ( ) ;
189+ layers_by_depth [ layer . Value ] . append ( layer . Key ) ;
190+ }
191+
192+ // Get sorted list of layer depths.
193+ var depth_keys = layers_by_depth . Keys . Reverse ( ) ;
194+
195+ // Set self.layers ordered by depth.
196+ var layers = new List < Layer > ( ) ;
197+ foreach ( var depth in depth_keys )
198+ {
199+ var layers_for_depth = layers_by_depth [ depth ] ;
200+
201+ // Network.layers needs to have a deterministic order:
202+ // here we order them by traversal order.
203+ layers_for_depth . Reverse ( ) ;
204+ layers . AddRange ( layers_for_depth ) ;
205+ }
206+
207+ // Get sorted list of node depths.
208+ depth_keys = nodes_by_depth . Keys . Reverse ( ) ;
209+
210+ return ( network_nodes , nodes_by_depth , layers , layers_by_depth ) ;
75211 }
76212
77- Tensors run_internal_graph ( Tensors inputs , Tensor state = null , bool is_training = false )
213+ string MakeNodeKey ( string layer_name , int node_index )
214+ => $ "{ layer_name } _ib-{ node_index } ";
215+
216+ /// <summary>
217+ /// This method topologically sorts nodes in order from inputs to outputs.
218+ /// </summary>
219+ /// <param name="outputs"></param>
220+ ( List < Node > , Dictionary < Layer , int > ) BuildMap ( Tensors outputs )
78221 {
222+ var finished_nodes = new List < Node > ( ) ;
223+ var nodes_in_progress = new List < Node > ( ) ;
224+ var nodes_in_decreasing_depth = new List < Node > ( ) ;
225+ var layer_indices = new Dictionary < Layer , int > ( ) ;
226+ foreach ( var output in outputs )
227+ BuildMapHelper ( output ,
228+ finished_nodes ,
229+ nodes_in_progress ,
230+ nodes_in_decreasing_depth ,
231+ layer_indices ) ;
232+
233+ return ( nodes_in_decreasing_depth , layer_indices ) ;
234+ }
235+
236+ void BuildMapHelper ( Tensor tensor ,
237+ List < Node > finished_nodes ,
238+ List < Node > nodes_in_progress ,
239+ List < Node > nodes_in_decreasing_depth ,
240+ Dictionary < Layer , int > layer_indices )
241+ {
242+ var ( layer , node_index , _) = tensor . KerasHistory ;
243+ var node = layer . InboundNodes [ node_index ] ;
244+
245+ // Don't repeat work for shared subgraphs
246+ if ( finished_nodes . Contains ( node ) )
247+ return ;
248+
249+ // Prevent cycles.
250+ if ( nodes_in_progress . Contains ( node ) )
251+ throw new ValueError ( $ "The tensor { tensor . name } at layer { layer . Name } is part of a cycle.") ;
252+
253+ // Store the traversal order for layer sorting.
254+ if ( ! layer_indices . ContainsKey ( layer ) )
255+ layer_indices [ layer ] = layer_indices . Count ;
256+
257+ // Propagate to all previous tensors connected to this node.
258+ nodes_in_progress . Add ( node ) ;
259+ foreach ( var k_tensor in node . KerasInputs )
260+ BuildMapHelper ( k_tensor ,
261+ finished_nodes ,
262+ nodes_in_progress ,
263+ nodes_in_decreasing_depth ,
264+ layer_indices ) ;
265+
266+ finished_nodes . Add ( node ) ;
267+ nodes_in_progress . Remove ( node ) ;
268+ nodes_in_decreasing_depth . Insert ( nodes_in_decreasing_depth . Count , node ) ;
269+ }
270+
271+ protected override Tensors CallFn ( Tensors inputs , Tensor state = null , bool is_training = false )
272+ {
273+ return run_internal_graph ( inputs , is_training ) ;
274+ }
275+
276+ Tensors run_internal_graph ( Tensors inputs , bool training = false , Tensors mask = null )
277+ {
278+ if ( mask != null )
279+ {
280+ Tensor [ ] masks = new Tensor [ inputs . Count ( ) ] ;
281+ foreach ( var ( i , input_t ) in enumerate ( inputs ) )
282+ input_t . KerasMask = masks [ i ] ;
283+ }
284+
285+ var tensor_dict = new Dictionary < int , Tensor [ ] > ( ) ;
286+ foreach ( var ( x , y ) in zip ( this . inputs , inputs ) )
287+ {
288+ var y1 = conform_to_reference_input ( y , x ) ;
289+ var x_id = x . GetHashCode ( ) ;
290+ tensor_dict [ x_id ] = Enumerable . Range ( 0 , tensor_usage_count [ x_id ] ) . Select ( x => y1 ) . ToArray ( ) ;
291+ }
292+
293+ var depth_keys = NodesByDepth . Keys . Reverse ( ) . ToArray ( ) ;
294+
295+ foreach ( var depth in depth_keys )
296+ {
297+ var nodes = NodesByDepth [ depth ] ;
298+ foreach ( var node in nodes )
299+ {
300+ // Input tensors already exist.
301+ if ( node . IsInput )
302+ continue ;
303+
304+ var layer_inputs = new Tensors ( tensor_dict [ node . FlatInputIds [ 0 ] ] ) ;
305+ tensor_dict [ node . FlatInputIds [ 0 ] ] = new Tensor [ 0 ] ;
306+
307+ var outputs = node . Layer . Apply ( layer_inputs , is_training : training ) ;
308+ // Update tensor_dict.
309+ foreach ( var ( x_id , y ) in zip ( node . FlatOutputIds , outputs ) )
310+ tensor_dict [ x_id ] = Enumerable . Range ( 0 , tensor_usage_count [ x_id ] ) . Select ( x => y ) . ToArray ( ) ;
311+ }
312+ }
313+
314+ foreach ( var x in outputs )
315+ {
316+
317+ }
79318 throw new NotImplementedException ( "" ) ;
80319 }
320+
321+ Tensor conform_to_reference_input ( Tensor tensor , Tensor ref_input )
322+ {
323+ return tensor ;
324+ }
81325 }
82326}
0 commit comments