Coverage Report

Created: 2017-11-12 13:27

/home/liu/buildslave/linux-x64-runtests/build/lib/nnc/ccv_nnc_symbolic_graph.c
Line
Count
Source (jump to first uncovered line)
1
#include "ccv_nnc.h"
2
#include "ccv_nnc_easy.h"
3
#include "ccv_nnc_internal.h"
4
#include "ccv_internal.h"
5
#ifdef HAVE_CUDA
6
#include "gpu/ccv_nnc_compat.h"
7
#endif
8
#include "_ccv_nnc_symbolic_graph.h"
9
10
const ccv_nnc_tensor_param_t ccv_nnc_tensor_auto = {};
11
12
int ccv_nnc_is_tensor_auto(const ccv_nnc_tensor_param_t params)
13
1.23k
{
14
1.23k
  return (memcmp(&params, &ccv_nnc_tensor_auto, sizeof(ccv_nnc_tensor_param_t)) == 0);
15
1.23k
}
16
17
ccv_nnc_symbolic_graph_t* ccv_nnc_symbolic_graph_new(void)
18
36
{
19
36
  ccv_nnc_symbolic_graph_t* graph = cccalloc(1, sizeof(ccv_nnc_symbolic_graph_t));
20
36
  graph->tensor_symbol_info = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_info_t), 5, 0);
21
36
  graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), 5, 0);
22
36
  return graph;
23
36
}
24
25
ccv_nnc_symbolic_graph_t* ccv_nnc_symbolic_graph_dup(const ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_symbolic_graph_subst_f subst)
26
5
{
27
5
  ccv_nnc_symbolic_graph_t* new_graph = ccmalloc(sizeof(ccv_nnc_symbolic_graph_t));
28
5
  memcpy(new_graph, graph, sizeof(ccv_nnc_symbolic_graph_t));
29
5
  new_graph->tensor_symbol_info = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_info_t), graph->tensor_symbol_info->rnum, 0);
30
5
  new_graph->tensor_symbol_info->rnum = graph->tensor_symbol_info->rnum;
31
5
  memcpy(
ccv_array_get5
(new_graph->tensor_symbol_info, 0),
ccv_array_get5
(graph->tensor_symbol_info, 0), sizeof(ccv_nnc_tensor_symbol_info_t) * graph->tensor_symbol_info->rnum);
32
5
  int i;
33
35
  for (i = 0; 
i < new_graph->tensor_symbol_info->rnum35
;
i++30
)
34
30
  {
35
30
    ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(new_graph->tensor_symbol_info, i);
36
30
    if (symbol_info->name)
37
30
    {
38
30
      char* const name = symbol_info->name;
39
30
      const size_t n = strnlen(name, 63) + 1;
40
30
      symbol_info->name = (char*)ccmalloc(n);
41
30
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
42
30
      strncpy(symbol_info->name, name, n);
43
30
    }
44
30
    if (symbol_info->s_ref)
45
2
    {
46
2
      ccv_array_t* const s_ref = symbol_info->s_ref;
47
2
      symbol_info->s_ref = ccv_array_new(sizeof(int), s_ref->rnum, 0);
48
2
      symbol_info->s_ref->rnum = s_ref->rnum;
49
2
      memcpy(
ccv_array_get2
(symbol_info->s_ref, 0),
ccv_array_get2
(s_ref, 0), sizeof(int) * s_ref->rnum);
50
2
    }
51
30
  }
52
5
  new_graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), graph->exec_symbol_info->rnum, 0);
53
5
  new_graph->exec_symbol_info->rnum = graph->exec_symbol_info->rnum;
54
5
  memcpy(
ccv_array_get5
(new_graph->exec_symbol_info, 0),
ccv_array_get5
(graph->exec_symbol_info, 0), sizeof(ccv_nnc_graph_exec_symbol_info_t) * graph->exec_symbol_info->rnum);
55
17
  for (i = 0; 
i < new_graph->exec_symbol_info->rnum17
;
i++12
)
56
12
  {
57
12
    ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(new_graph->exec_symbol_info, i);
58
12
    if (symbol_info->name)
59
12
    {
60
12
      char* const name = symbol_info->name;
61
12
      const size_t n = strnlen(name, 63) + 1;
62
12
      symbol_info->name = (char*)ccmalloc(n);
63
12
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
64
12
      strncpy(symbol_info->name, name, n);
65
12
    }
66
12
    if (symbol_info->outgoings)
67
7
    {
68
7
      ccv_array_t* const outgoings = symbol_info->outgoings;
69
7
      symbol_info->outgoings = ccv_array_new(sizeof(int), outgoings->rnum, 0);
70
7
      symbol_info->outgoings->rnum = outgoings->rnum;
71
7
      memcpy(
ccv_array_get7
(symbol_info->outgoings, 0),
ccv_array_get7
(outgoings, 0), sizeof(int) * outgoings->rnum);
72
7
    }
73
12
    if (symbol_info->inputs)
74
7
    {
75
7
      int* const inputs = symbol_info->inputs;
76
7
      symbol_info->inputs = (int*)ccmalloc(sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
77
7
      symbol_info->outputs = symbol_info->inputs + symbol_info->input_size;
78
7
      memcpy(symbol_info->inputs, inputs, sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
79
7
    }
80
12
  }
81
5
  if (graph->sources)
82
5
  {
83
5
    new_graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->sources->rnum, 0);
84
5
    new_graph->sources->rnum = graph->sources->rnum;
85
5
    memcpy(
ccv_array_get5
(new_graph->sources, 0),
ccv_array_get5
(graph->sources, 0), sizeof(ccv_nnc_graph_exec_symbol_t) * graph->sources->rnum);
86
10
    for (i = 0; 
i < new_graph->sources->rnum10
;
i++5
)
87
5
      
((ccv_nnc_graph_exec_symbol_t*)5
ccv_array_get5
(new_graph->sources, i))->graph = new_graph;
88
5
  }
89
5
  if (graph->destinations)
90
5
  {
91
5
    new_graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->destinations->rnum, 0);
92
5
    new_graph->destinations->rnum = graph->destinations->rnum;
93
5
    memcpy(
ccv_array_get5
(new_graph->destinations, 0),
ccv_array_get5
(graph->destinations, 0), sizeof(ccv_nnc_graph_exec_symbol_t) * graph->destinations->rnum);
94
10
    for (i = 0; 
i < new_graph->destinations->rnum10
;
i++5
)
95
5
      
((ccv_nnc_graph_exec_symbol_t*)5
ccv_array_get5
(new_graph->destinations, i))->graph = new_graph;
96
5
  }
97
5
  if (graph->breakpoints)
98
5
  {
99
5
    new_graph->breakpoints = (ccv_nnc_graph_exec_symbol_t*)ccmalloc(sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
100
5
    memcpy(new_graph->breakpoints, graph->breakpoints, sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
101
10
    for (i = 0; 
i < graph->breakpoint_size10
;
i++5
)
102
5
      new_graph->breakpoints[i].graph = new_graph;
103
5
  }
104
5
  if (graph->backward_tensor_symbols)
105
0
  {
106
0
    new_graph->backward_tensor_symbols = (int*)ccmalloc(sizeof(int) * (new_graph->forward_symbol_size + new_graph->backward_symbol_size));
107
0
    if (new_graph->forward_symbol_size > 0)
108
0
      memcpy(new_graph->backward_tensor_symbols, graph->backward_tensor_symbols, sizeof(int) * new_graph->forward_symbol_size);
109
0
    new_graph->backward_exec_symbols = new_graph->backward_tensor_symbols + new_graph->forward_symbol_size;
110
0
    if (new_graph->backward_symbol_size > 0)
111
0
      memcpy(new_graph->backward_exec_symbols, graph->backward_exec_symbols, sizeof(int) * new_graph->backward_symbol_size);
112
0
  }
113
5
  if (subst)
114
5
  {
115
17
    for (i = 0; 
i < new_graph->exec_symbol_info->rnum17
;
i++12
)
116
12
    {
117
12
      ccv_nnc_graph_exec_symbol_info_t* const symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(new_graph->exec_symbol_info, i);
118
12
      if (!symbol_info->dead)
119
12
      {
120
12
        symbol_info->cmd = subst((ccv_nnc_graph_exec_symbol_t){
121
12
          .d = i,
122
12
          .graph = graph,
123
12
        }, symbol_info->cmd);
124
12
        if (
symbol_info->cmd.cmd != CCV_NNC_GRAPH_FORWARD && 12
symbol_info->cmd.cmd != CCV_NNC_GRAPH_BACKWARD12
)
125
12
          symbol_info->graph_ref = 0;
126
12
      }
127
12
    }
128
5
  }
129
5
  // TODO: See how and if I need to dup sub-graphs. I also need to figure out what's the relationship between this graph
130
5
  // and its parent graph (or how can we use the symbol from the graph properly).
131
5
  new_graph->sub_graphs = 0;
132
5
  return new_graph;
133
5
}
134
135
ccv_nnc_tensor_symbol_t ccv_nnc_tensor_symbol_new(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_param_t info, const char* const name)
136
328
{
137
328
  ccv_nnc_tensor_symbol_t symbol = {
138
328
    .info = info,
139
328
    .d = graph->tensor_symbol_info->rnum,
140
328
    .graph = graph
141
328
  };
142
328
  ccv_nnc_tensor_symbol_info_t symbol_info = {
143
328
    .info = info,
144
328
  };
145
328
  if (name)
146
152
  {
147
152
    size_t n = strnlen(name, 63) + 1;
148
152
    symbol_info.name = (char*)ccmalloc(n);
149
152
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
150
152
    strncpy(symbol_info.name, name, n);
151
152
  }
152
328
  ccv_array_push(graph->tensor_symbol_info, &symbol_info);
153
328
  return symbol;
154
328
}
155
156
ccv_nnc_tensor_symbol_t ccv_nnc_tensor_symbol_alias_new(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_symbol, const int ofs[CCV_NNC_MAX_DIM_ALLOC], const int inc[CCV_NNC_MAX_DIM_ALLOC], const ccv_nnc_tensor_param_t info, const char* const name)
157
108
{
158
108
  assert(tensor_symbol.graph == graph);
159
108
  int d = tensor_symbol.d;
160
108
  assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
161
108
  ccv_nnc_tensor_symbol_info_t* info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
162
108
  // Find the root tensor that is not an alias.
163
108
  while (info_d->alias_ref)
164
0
  {
165
0
    d = info_d->alias_ref - 1;
166
0
    assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
167
0
    info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
168
0
  }
169
108
  ccv_nnc_tensor_symbol_t alias = {
170
108
    .info = info,
171
108
    .d = graph->tensor_symbol_info->rnum,
172
108
    .graph = graph
173
108
  };
174
108
  // Alias comes in two shapes: 1). the total tensor count is strictly smaller or equal to, and without ofs; 2). with ofs, and each dimension is strictly smaller or equal to.
175
108
  int i;
176
972
  for (i = 0; 
i < 972
CCV_NNC_MAX_DIM_ALLOC972
;
i++864
)
177
864
  {
178
864
    assert(info.dim[i] + ofs[i] <= inc[i]);
179
864
  }
180
108
  assert(ccv_nnc_dimension_count(inc) <= ccv_nnc_tensor_count(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d))->info));
181
108
  ccv_nnc_tensor_symbol_info_t alias_info = {
182
108
    .alias_ref = d + 1,
183
108
    .info = info,
184
108
  };
185
108
  if (name)
186
52
  {
187
52
    size_t n = strnlen(name, 63) + 1;
188
52
    alias_info.name = (char*)ccmalloc(n);
189
52
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
190
52
    strncpy(alias_info.name, name, n);
191
52
  }
192
108
  memcpy(alias_info.ofs, ofs, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
193
108
  memcpy(alias_info.inc, inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
194
108
  ccv_array_push(graph->tensor_symbol_info, &alias_info);
195
108
  return alias;
196
108
}
197
198
ccv_nnc_tensor_symbol_t ccv_nnc_tensor_symbol_resolve_alias(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_alias)
199
0
{
200
0
  assert(graph == tensor_alias.graph);
201
0
  assert(tensor_alias.d < graph->tensor_symbol_info->rnum);
202
0
  ccv_nnc_tensor_symbol_info_t* alias_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor_alias.d);
203
0
  assert(alias_info->alias_ref);
204
0
  ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, alias_info->alias_ref - 1);
205
0
  ccv_nnc_tensor_symbol_t symbol = {
206
0
    .info = symbol_info->info,
207
0
    .d = alias_info->alias_ref - 1,
208
0
    .graph = graph
209
0
  };
210
0
  return symbol;
211
0
}
212
213
// This method generate tensor symbols and their links along the way when traverse the graph.
214
enum {
215
  MAP_TENSOR_USE_AS_INPUT,
216
  MAP_TENSOR_USE_AS_OUTPUT,
217
};
218
219
static void _ccv_nnc_graph_exec_add_input_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
220
12
{
221
12
  int i;
222
15
  for (i = 0; 
i < exec_symbol_info->input_size15
;
i++3
)
223
3
    
if (3
exec_symbol_info->inputs[i] == d3
)
224
0
      return; // No need to continue, this symbol already exists as input.
225
12
  // Expand the array.
226
12
  
if (12
!exec_symbol_info->input_size && 12
!exec_symbol_info->output_size9
)
227
9
  {
228
9
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
229
9
    exec_symbol_info->inputs[0] = d;
230
9
    exec_symbol_info->input_size = 1;
231
9
    exec_symbol_info->outputs = exec_symbol_info->inputs + 1;
232
9
    return;
233
9
  }
234
3
  
exec_symbol_info->inputs = (int*)3
ccrealloc3
(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + 1 + exec_symbol_info->output_size));
235
3
  if (exec_symbol_info->output_size)
236
0
    memmove(exec_symbol_info->outputs + 1, exec_symbol_info->outputs, sizeof(int) * exec_symbol_info->output_size); 
237
3
  exec_symbol_info->inputs[exec_symbol_info->input_size] = d;
238
3
  ++exec_symbol_info->input_size;
239
3
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
240
3
}
241
242
static void _ccv_nnc_graph_exec_add_output_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
243
6
{
244
6
  int i;
245
6
  for (i = 0; 
i < exec_symbol_info->output_size6
;
i++0
)
246
0
    
if (0
exec_symbol_info->outputs[i] == d0
)
247
0
      return; // No need to continue, this symbol already exists as output.
248
6
  // Expand the array.
249
6
  
if (6
!exec_symbol_info->input_size && 6
!exec_symbol_info->output_size0
)
250
0
  {
251
0
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
252
0
    exec_symbol_info->outputs = exec_symbol_info->inputs;
253
0
    exec_symbol_info->outputs[0] = d;
254
0
    exec_symbol_info->output_size = 1;
255
0
    return;
256
0
  }
257
6
  
exec_symbol_info->inputs = (int*)6
ccrealloc6
(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + exec_symbol_info->output_size + 1));
258
6
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
259
6
  exec_symbol_info->outputs[exec_symbol_info->output_size] = d;
260
6
  ++exec_symbol_info->output_size;
261
6
}
262
263
void ccv_nnc_tensor_symbol_set_peer(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_symbol, const ccv_nnc_tensor_symbol_t peer_tensor_symbol)
264
4
{
265
4
  assert(tensor_symbol.graph == graph);
266
4
  assert(tensor_symbol.d >= 0);
267
4
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
268
4
  assert(peer_tensor_symbol.graph == graph->peer);
269
4
  assert(peer_tensor_symbol.d >= 0);
270
4
  assert(peer_tensor_symbol.d < graph->peer->tensor_symbol_info->rnum);
271
4
  ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor_symbol.d);
272
4
  tensor_info->peer_ref = peer_tensor_symbol.d + 1;
273
4
}
274
275
void ccv_nnc_tensor_symbol_pass(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_symbolic_graph_t* const sub_graph, const ccv_nnc_tensor_symbol_t tensor_symbol, const ccv_nnc_tensor_symbol_t sub_tensor_symbol)
276
2
{
277
2
  assert(sub_graph->p == graph);
278
2
  assert(tensor_symbol.graph == graph);
279
2
  assert(sub_tensor_symbol.graph == sub_graph);
280
2
  assert(tensor_symbol.d >= 0);
281
2
  assert(sub_tensor_symbol.d >= 0);
282
2
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
283
2
  assert(sub_tensor_symbol.d < sub_graph->tensor_symbol_info->rnum);
284
2
  ccv_nnc_tensor_symbol_info_t* const sub_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(sub_graph->tensor_symbol_info, sub_tensor_symbol.d);
285
2
  sub_tensor_info->p_ref = tensor_symbol.d + 1;
286
2
  ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor_symbol.d);
287
2
  if (!tensor_info->s_ref)
288
2
  {
289
2
    tensor_info->s_ref = ccv_array_new(sizeof(int), graph->sub_graphs->rnum, 0);
290
2
    tensor_info->s_ref->rnum = graph->sub_graphs->rnum;
291
2
    ccv_array_zero(tensor_info->s_ref);
292
0
  } else 
if (0
tensor_info->s_ref->rnum != graph->sub_graphs->rnum0
)
293
0
    ccv_array_resize(tensor_info->s_ref, graph->sub_graphs->rnum);
294
2
  const int p_idx = sub_graph->p_idx - 1;
295
2
  assert(p_idx >= 0 && p_idx < tensor_info->s_ref->rnum);
296
2
  const int s_idx = *(int*)ccv_array_get(tensor_info->s_ref, p_idx);
297
2
  assert(s_idx == 0); // Otherwise it is assigned before
298
2
  *(int*)ccv_array_get(tensor_info->s_ref, p_idx) = sub_tensor_symbol.d + 1;
299
2
}
300
301
static int _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t symbol, const int map_use)
302
18
{
303
18
  assert(graph && symbol.graph);
304
18
  assert(symbol.graph != graph);
305
18
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(symbol.graph->tensor_symbol_info, symbol.d);
306
18
  assert(!symbol_info->alias_ref);
307
18
  // Find if the symbol is in the sub-graph.
308
18
  const ccv_nnc_symbolic_graph_t* curr_graph = symbol.graph;
309
18
  assert(symbol.d >= 0 && symbol.d < curr_graph->tensor_symbol_info->rnum);
310
38
  while (
curr_graph && 38
curr_graph != graph25
)
311
20
    curr_graph = curr_graph->p;
312
18
  if (curr_graph)
313
5
  {
314
5
    // The graph is a parent of the symbol passed in. For this case, if we are connecting this symbol to an exec as input,
315
5
    // that means it must be an output in these sub-graphs. Otherwise, if we are connecting this symbol to an exec as output,
316
5
    // it must be an input in these sub-graphs.
317
5
    curr_graph = symbol.graph;
318
5
    ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
319
5
    ccv_nnc_tensor_symbol_t curr_symbol = symbol;
320
10
    while (curr_graph != graph)
321
5
    {
322
5
      ccv_nnc_symbolic_graph_t* const p = curr_graph->p;
323
5
      // I need to find the symbol whether it exists or not before creating new one.
324
5
      ccv_nnc_tensor_symbol_t new_symbol;
325
5
      ccv_nnc_tensor_symbol_info_t* new_symbol_info;
326
5
      if (!curr_symbol_info->p_ref)
327
5
      {
328
5
        new_symbol = ccv_nnc_tensor_symbol_new(p, curr_symbol_info->info, curr_symbol_info->name);
329
5
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
330
5
        curr_symbol_info->p_ref = new_symbol.d + 1;
331
5
        new_symbol_info->s_ref = ccv_array_new(sizeof(int), p->sub_graphs->rnum, 0);
332
5
        new_symbol_info->s_ref->rnum = p->sub_graphs->rnum;
333
5
        ccv_array_zero(new_symbol_info->s_ref);
334
5
        *(int*)ccv_array_get(new_symbol_info->s_ref, curr_graph->p_idx - 1) = curr_symbol.d + 1;
335
0
      } else {
336
0
        new_symbol.d = curr_symbol_info->p_ref - 1;
337
0
        new_symbol.graph = p;
338
0
        assert(new_symbol.d >= 0 && new_symbol.d < p->tensor_symbol_info->rnum);
339
0
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
340
0
      }
341
5
      if (curr_graph->exec_idx)
342
5
      {
343
5
        // This is a sub-graph.
344
5
        assert(p);
345
5
        assert(curr_graph->exec_idx > 0 && curr_graph->exec_idx <= p->exec_symbol_info->rnum);
346
5
        ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(p->exec_symbol_info, curr_graph->exec_idx - 1);
347
5
        switch (map_use)
348
5
        {
349
5
          case MAP_TENSOR_USE_AS_INPUT:
350
5
            _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, new_symbol.d);
351
5
            break;
352
0
          case MAP_TENSOR_USE_AS_OUTPUT:
353
0
            _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, new_symbol.d);
354
0
            break;
355
5
        }
356
5
      }
357
5
      // Move on.
358
5
      curr_symbol = new_symbol;
359
5
      curr_symbol_info = new_symbol_info;
360
5
      curr_graph = p;
361
5
    }
362
5
    return curr_symbol.d;
363
5
  }
364
18
  // Otherwise, if the symbol is in the parent graph, this is a bit more expensive because I need to keep a trace stack.
365
13
  curr_graph = graph;
366
13
  int d;
367
26
  for (d = 0; 
curr_graph && 26
curr_graph != symbol.graph26
;
d++13
)
368
13
    curr_graph = curr_graph->p;
369
13
  curr_graph = graph;
370
13
  int trace[d];
371
26
  for (d = 0; 
curr_graph && 26
curr_graph != symbol.graph26
;
d++13
)
372
13
  {
373
13
    const int p_idx = curr_graph->p_idx - 1;
374
13
    trace[d] = p_idx;
375
13
    curr_graph = curr_graph->p;
376
13
  }
377
13
  // If it is not in both the parent graph and the sub-graph, the input is invalid.
378
13
  assert(curr_graph);
379
13
  curr_graph = symbol.graph;
380
13
  ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
381
13
  ccv_nnc_tensor_symbol_t curr_symbol = symbol;
382
13
  // The graph is a sub graph of the symbol passed in. For this case, if we are connecting this symbol to an exec as input,
383
13
  // that means it must be an input in these parent graphs. Otherwise, if we are connecting this symbol to an exec as output,
384
13
  // it must be an output in these parent graphs.
385
13
  int i;
386
26
  for (i = d - 1; 
i >= 026
;
i--13
)
387
13
  {
388
13
    const int p_idx = trace[i];
389
13
    assert(p_idx >= 0);
390
13
    assert(curr_graph->sub_graphs);
391
13
    if (!curr_symbol_info->s_ref)
392
13
    {
393
13
      curr_symbol_info->s_ref = ccv_array_new(sizeof(int), curr_graph->sub_graphs->rnum, 0);
394
13
      curr_symbol_info->s_ref->rnum = curr_graph->sub_graphs->rnum;
395
13
      ccv_array_zero(curr_symbol_info->s_ref);
396
0
    } else 
if (0
curr_symbol_info->s_ref->rnum != curr_graph->sub_graphs->rnum0
)
397
0
      ccv_array_resize(curr_symbol_info->s_ref, curr_graph->sub_graphs->rnum);
398
13
    assert(p_idx >= 0 && p_idx < curr_symbol_info->s_ref->rnum);
399
13
    const int s_idx = *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx);
400
13
    ccv_nnc_symbolic_graph_t* const s = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(curr_graph->sub_graphs, p_idx);
401
13
    ccv_nnc_tensor_symbol_t new_symbol;
402
13
    ccv_nnc_tensor_symbol_info_t* new_symbol_info;
403
13
    // I need to find the symbol whether it exists or not before creating new one.
404
13
    if (!s_idx)
405
13
    {
406
13
      new_symbol = ccv_nnc_tensor_symbol_new(s, symbol_info->info, symbol_info->name);
407
13
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
408
13
      new_symbol_info->p_ref = curr_symbol.d + 1;
409
13
      *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx) = new_symbol.d + 1;
410
0
    } else {
411
0
      new_symbol.d = s_idx - 1;
412
0
      new_symbol.graph = s;
413
0
      assert(new_symbol.d >= 0 && new_symbol.d < s->tensor_symbol_info->rnum);
414
0
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
415
0
    }
416
13
    if (s->exec_idx)
417
13
    {
418
13
      assert(s->p); // This is a sub-graph.
419
13
      assert(s->exec_idx > 0 && s->exec_idx <= curr_graph->exec_symbol_info->rnum);
420
13
      ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(curr_graph->exec_symbol_info, s->exec_idx - 1);
421
13
      switch (map_use)
422
13
      {
423
12
        case MAP_TENSOR_USE_AS_INPUT:
424
12
          _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, curr_symbol.d);
425
12
          break;
426
1
        case MAP_TENSOR_USE_AS_OUTPUT:
427
1
          _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, curr_symbol.d);
428
1
          break;
429
13
      }
430
13
    }
431
13
    // Move on.
432
13
    curr_symbol = new_symbol;
433
13
    curr_symbol_info = new_symbol_info;
434
13
    curr_graph = s;
435
13
  }
436
13
  return curr_symbol.d;
437
13
}
438
439
static int _ccv_nnc_symbolic_graph_map_tensor_symbol(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t symbol, const int map_use)
440
18
{
441
18
  assert(graph && symbol.graph);
442
18
  assert(symbol.graph != graph);
443
18
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(symbol.graph->tensor_symbol_info, symbol.d);
444
18
  if (!symbol_info->alias_ref)
445
17
    return _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, symbol, map_use);
446
1
  const int d = symbol_info->alias_ref - 1;
447
1
  assert(d >= 0 && d < symbol.graph->tensor_symbol_info->rnum);
448
1
  const int map_d = _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, (ccv_nnc_tensor_symbol_t){
449
1
    .graph = symbol.graph,
450
1
    .d = d
451
1
  }, map_use);
452
1
  const ccv_nnc_tensor_symbol_t alias = ccv_nnc_tensor_symbol_alias_new(graph, (ccv_nnc_tensor_symbol_t){
453
1
    .graph = graph,
454
1
    .d = map_d
455
1
  }, symbol_info->ofs, symbol_info->inc, symbol_info->info, symbol_info->name);
456
1
  return alias.d;
457
18
}
458
459
static void _ccv_nnc_graph_exec_symbol_set_io(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_graph_exec_symbol_info_t* const exec_info, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, const ccv_nnc_tensor_symbol_t* const outputs, const int output_size)
460
206
{
461
206
  exec_info->input_size = input_size;
462
206
  exec_info->output_size = output_size;
463
206
  if (
input_size > 0 || 206
output_size > 032
)
464
174
  {
465
174
    if (!exec_info->inputs)
466
174
      
exec_info->inputs = 174
ccmalloc174
(sizeof(int) * (input_size + output_size));
467
174
    else
468
0
      
exec_info->inputs = 0
ccrealloc0
(exec_info->inputs, sizeof(int) * (input_size + output_size));
469
174
    exec_info->outputs = exec_info->inputs + input_size;
470
174
  }
471
206
  int i;
472
634
  for (i = 0; 
i < input_size634
;
i++428
)
473
428
  {
474
411
    const int d = (inputs[i].graph != graph && 
inputs[i].d >= 071
) ?
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, inputs[i], MAP_TENSOR_USE_AS_INPUT)17
:
inputs[i].d411
;
475
428
    exec_info->inputs[i] = d;
476
428
  }
477
415
  for (i = 0; 
i < output_size415
;
i++209
)
478
209
  {
479
208
    const int d = (outputs[i].graph != graph && 
outputs[i].d >= 04
) ?
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, outputs[i], MAP_TENSOR_USE_AS_OUTPUT)1
:
outputs[i].d208
;
480
209
    exec_info->outputs[i] = d;
481
209
  }
482
206
}
483
484
ccv_nnc_graph_exec_symbol_t ccv_nnc_graph_exec_symbol_new(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_cmd_t cmd, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, const ccv_nnc_tensor_symbol_t* const outputs, const int output_size, const char* const name)
485
205
{
486
205
  ccv_nnc_graph_exec_symbol_t symbol = {
487
205
    .d = graph->exec_symbol_info->rnum,
488
205
    .graph = graph
489
205
  };
490
205
  ccv_nnc_graph_exec_symbol_info_t symbol_info = {
491
205
    .cmd = cmd,
492
205
    .hint = ccv_nnc_no_hint,
493
205
  };
494
205
  if (name)
495
93
  {
496
93
    size_t n = strnlen(name, 63) + 1;
497
93
    symbol_info.name = (char*)ccmalloc(n);
498
93
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
499
93
    strncpy(symbol_info.name, name, n);
500
93
  }
501
205
  _ccv_nnc_graph_exec_symbol_set_io(graph, &symbol_info, inputs, input_size, outputs, output_size);
502
205
  ccv_array_push(graph->exec_symbol_info, &symbol_info);
503
205
  return symbol;
504
205
}
505
506
void ccv_nnc_graph_exec_symbol_set_io(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, const ccv_nnc_tensor_symbol_t* const outputs, const int output_size)
507
1
{
508
1
  assert(exec.graph == graph);
509
1
  assert(exec.d >= 0);
510
1
  assert(exec.d < graph->exec_symbol_info->rnum);
511
1
  ccv_nnc_graph_exec_symbol_info_t* const exec_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, exec.d);
512
1
  _ccv_nnc_graph_exec_symbol_set_io(graph, exec_info, inputs, input_size, outputs, output_size);
513
1
}
514
515
ccv_nnc_cmd_t ccv_nnc_graph_exec_symbol_cmd(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec)
516
2
{
517
2
  assert(graph == exec.graph);
518
2
  assert(exec.d < graph->exec_symbol_info->rnum);
519
2
  ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, exec.d);
520
2
  return symbol_info->cmd;
521
2
}
522
523
int ccv_nnc_graph_exec_symbol_set_hint(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec, const ccv_nnc_hint_t hint)
524
0
{
525
0
  assert(graph == exec.graph);
526
0
  assert(exec.d < graph->exec_symbol_info->rnum);
527
0
  ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, exec.d);
528
0
  symbol_info->hint = hint;
529
0
  return 0;
530
0
}
531
532
int ccv_nnc_tensor_symbol_set(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const ccv_nnc_tensor_param_t info)
533
0
{
534
0
  assert(graph == tensor.graph);
535
0
  assert(tensor.d < graph->tensor_symbol_info->rnum);
536
0
  ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
537
0
  symbol_info->info = info;
538
0
  return 0;
539
0
}
540
541
int ccv_nnc_tensor_symbol_set_flags(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const int flags)
542
98
{
543
98
  assert(graph == tensor.graph);
544
98
  assert(tensor.d < graph->tensor_symbol_info->rnum);
545
98
  ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
546
98
  symbol_info->flags = flags;
547
98
  return 0;
548
98
}
549
550
int ccv_nnc_tensor_symbol_flag(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const int flags)
551
4
{
552
4
  assert(graph == tensor.graph);
553
4
  assert(tensor.d < graph->tensor_symbol_info->rnum);
554
4
  ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
555
4
  return !!(symbol_info->flags & flags);
556
4
}
557
558
int ccv_nnc_graph_exec_symbol_concat(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t source, const ccv_nnc_graph_exec_symbol_t destination)
559
248
{
560
248
  assert(graph == source.graph);
561
248
  assert(graph == destination.graph);
562
248
  assert(source.d < graph->exec_symbol_info->rnum);
563
248
  assert(destination.d < graph->exec_symbol_info->rnum);
564
248
  ccv_nnc_graph_exec_symbol_info_t* src_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, source.d);
565
248
  if (!src_symbol_info->outgoings)
566
166
    src_symbol_info->outgoings = ccv_array_new(sizeof(int32_t), 1, 0);
567
82
  else {
568
82
    int i;
569
82
    // Check if this is already connected, if so, skip.
570
186
    for (i = 0; 
i < src_symbol_info->outgoings->rnum186
;
i++104
)
571
114
      
if (114
*(int*)114
ccv_array_get114
(src_symbol_info->outgoings, i) == destination.d)
572
10
        return -1;
573
82
  }
574
238
  ccv_array_push(src_symbol_info->outgoings, &destination.d);
575
238
  return 0;
576
248
}
577
578
int ccv_nnc_graph_exec_symbol_disjoin(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t source, const ccv_nnc_graph_exec_symbol_t destination)
579
0
{
580
0
  assert(graph == source.graph);
581
0
  assert(graph == destination.graph);
582
0
  assert(source.d < graph->exec_symbol_info->rnum);
583
0
  assert(destination.d < graph->exec_symbol_info->rnum);
584
0
  ccv_nnc_graph_exec_symbol_info_t* src_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, source.d);
585
0
  if (!src_symbol_info->outgoings)
586
0
    return -1;
587
0
  int i, j = -1;
588
0
  // Check if this is already connected, if so, skip.
589
0
  for (i = 0; 
i < src_symbol_info->outgoings->rnum0
;
i++0
)
590
0
    
if (0
*(int*)0
ccv_array_get0
(src_symbol_info->outgoings, i) == destination.d)
591
0
    {
592
0
      j = i;
593
0
      break;
594
0
    }
595
0
  if (j < 0)
596
0
    return -1;
597
0
  
if (0
j < src_symbol_info->outgoings->rnum - 10
)
598
0
    
*(int*)0
ccv_array_get0
(src_symbol_info->outgoings, j) = *(int*)
ccv_array_get0
(src_symbol_info->outgoings, src_symbol_info->outgoings->rnum - 1);
599
0
  --src_symbol_info->outgoings->rnum;
600
0
  return 0;
601
0
}
602
603
604
#define CCV_NNC_IS_AUTOGEN_ALL_EXECS(x) ((x) & CCV_NNC_AUTOGEN_ALL_EXECS)
604
33
#define CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(x) ((x) & CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS)
605
606
int ccv_nnc_graph_exec_symbol_autogen(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t* const execs, const int exec_size, const int flags)
607
33
{
608
33
  int i, j, x, y;
609
76
  for (i = 0; 
i < exec_size76
;
i++43
)
610
43
    
if (43
execs[i].graph == graph43
)
611
43
    {
612
43
      assert(execs[i].d >= 0);
613
43
      assert(execs[i].d < graph->exec_symbol_info->rnum);
614
43
    }
615
33
  if (
!33
CCV_NNC_IS_AUTOGEN_ALL_EXECS33
(flags) &&
exec_size12
)
616
11
    { assert(execs); }
617
33
  const int exec_total_size = 
CCV_NNC_IS_AUTOGEN_ALL_EXECS33
(flags) ?
graph->exec_symbol_info->rnum21
:
exec_size12
;
618
119
  for (i = 0; 
i < exec_total_size119
;
i++86
)
619
86
  {
620
86
    if (
!86
CCV_NNC_IS_AUTOGEN_ALL_EXECS86
(flags) &&
execs[i].graph != graph43
)
621
0
      continue;
622
86
    
int idx = 86
CCV_NNC_IS_AUTOGEN_ALL_EXECS86
(flags) ?
i43
:
execs[i].d43
;
623
86
    ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, idx);
624
86
    // Autogen for sub-graphs.
625
86
    if (symbol_info->graph_ref)
626
10
      
ccv_nnc_graph_exec_symbol_autogen(*(ccv_nnc_symbolic_graph_t**)10
ccv_array_get10
(graph->sub_graphs, symbol_info->graph_ref - 1), execs, exec_size, flags);
627
86
  }
628
119
  for (i = 0; 
i < exec_total_size119
;
i++86
)
629
86
  {
630
86
    if (
!86
CCV_NNC_IS_AUTOGEN_ALL_EXECS86
(flags) &&
execs[i].graph != graph43
)
631
0
      continue;
632
86
    
int a_idx = 86
CCV_NNC_IS_AUTOGEN_ALL_EXECS86
(flags) ?
i43
:
execs[i].d43
;
633
86
    ccv_nnc_graph_exec_symbol_info_t* a_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, a_idx);
634
183
    for (j = i + 1; 
j < exec_total_size183
;
j++97
)
635
97
    {
636
97
      if (
!97
CCV_NNC_IS_AUTOGEN_ALL_EXECS97
(flags) &&
execs[j].graph != graph69
)
637
0
        continue;
638
97
      
int b_idx = 97
CCV_NNC_IS_AUTOGEN_ALL_EXECS97
(flags) ?
j28
:
execs[j].d69
;
639
97
      // Skip if they are the same.
640
97
      if (a_idx == b_idx)
641
0
        continue;
642
97
      
ccv_nnc_graph_exec_symbol_info_t* b_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)97
ccv_array_get97
(graph->exec_symbol_info, b_idx);
643
97
      int b_to_a = 0;
644
256
      for (x = 0; 
x < a_symbol_info->input_size && 256
!b_to_a159
;
x++159
)
645
159
      {
646
159
        int a = a_symbol_info->inputs[x];
647
159
        // Handle alias as well.
648
159
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
649
159
        if (a_tensor_info->alias_ref)
650
48
          a = a_tensor_info->alias_ref - 1;
651
312
        for (y = 0; 
y < b_symbol_info->output_size && 312
!b_to_a153
;
y++153
)
652
153
        {
653
153
          int b = b_symbol_info->outputs[y];
654
153
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
655
153
          if (b_tensor_info->alias_ref)
656
44
            b = b_tensor_info->alias_ref - 1;
657
153
          if (a == b)
658
153
            // This two have matching inputs and outputs, thus, you can concat b to a.
659
0
            b_to_a = 1;
660
153
        }
661
159
      }
662
97
      if (b_to_a)
663
0
      {
664
0
        if (execs)
665
0
          ccv_nnc_graph_exec_symbol_concat(graph, execs[j], execs[i]);
666
0
        else
667
0
          ccv_nnc_graph_exec_symbol_concat(graph,
668
0
            (ccv_nnc_graph_exec_symbol_t) {
669
0
              .d = j,
670
0
              .graph = graph
671
0
            }, (ccv_nnc_graph_exec_symbol_t) {
672
0
              .d = i,
673
0
              .graph = graph
674
0
            }
675
0
          );
676
0
      }
677
97
      int a_to_b = 0;
678
184
      for (x = 0; 
x < a_symbol_info->output_size && 184
!a_to_b87
;
x++87
)
679
87
      {
680
87
        int a = a_symbol_info->outputs[x];
681
87
        // Handle alias as well.
682
87
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
683
87
        if (a_tensor_info->alias_ref)
684
27
          a = a_tensor_info->alias_ref - 1;
685
198
        for (y = 0; 
y < b_symbol_info->input_size && 198
!a_to_b132
;
y++111
)
686
111
        {
687
111
          int b = b_symbol_info->inputs[y];
688
111
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
689
111
          if (b_tensor_info->alias_ref)
690
48
            b = b_tensor_info->alias_ref - 1;
691
111
          if (a == b)
692
111
            // This two have matching inputs and outputs, thus, you can concat b to a.
693
54
            a_to_b = 1;
694
111
        }
695
87
      }
696
97
      if (a_to_b)
697
54
      {
698
54
        if (execs)
699
42
          ccv_nnc_graph_exec_symbol_concat(graph, execs[i], execs[j]);
700
54
        else
701
12
          ccv_nnc_graph_exec_symbol_concat(graph,
702
12
            (ccv_nnc_graph_exec_symbol_t) {
703
12
              .d = i,
704
12
              .graph = graph
705
12
            }, (ccv_nnc_graph_exec_symbol_t) {
706
12
              .d = j,
707
12
              .graph = graph
708
12
            }
709
12
          );
710
54
      }
711
97
    }
712
86
  }
713
33
  // If flag says so, loop over to find sources / destinations too.
714
33
  if (CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(flags))
715
22
  {
716
22
    int* flags = (int*)cccalloc(sizeof(int), graph->exec_symbol_info->rnum);
717
69
    for (i = 0; 
i < graph->exec_symbol_info->rnum69
;
i++47
)
718
47
    {
719
47
      ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
720
47
      if (
symbol_info->outgoings && 47
symbol_info->outgoings->rnum24
)
721
24
      {
722
24
        flags[i] |= 2;
723
49
        for (j = 0; 
j < symbol_info->outgoings->rnum49
;
j++25
)
724
25
          
flags[*(int*)25
ccv_array_get25
(symbol_info->outgoings, j)] |= 1;
725
24
      }
726
47
    }
727
22
    if (!graph->sources)
728
16
      graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
729
22
    else
730
6
      ccv_array_clear(graph->sources);
731
22
    if (!graph->destinations)
732
16
      graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
733
22
    else
734
6
      ccv_array_clear(graph->destinations);
735
69
    for (i = 0; 
i < graph->exec_symbol_info->rnum69
;
i++47
)
736
47
    {
737
47
      if (flags[i] == 3)
738
7
        continue;
739
40
      ccv_nnc_graph_exec_symbol_t exec = {
740
40
        .d = i,
741
40
        .graph = graph,
742
40
      };
743
40
      if (!(flags[i] & 1))
744
22
        ccv_array_push(graph->sources, &exec);
745
40
      if (!(flags[i] & 2))
746
23
        ccv_array_push(graph->destinations, &exec);
747
40
    }
748
22
    ccfree(flags);
749
22
  }
750
33
  return 0;
751
33
}
752
753
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_sources(const ccv_nnc_symbolic_graph_t* const graph)
754
24
{
755
24
  return graph->sources ? 
(ccv_nnc_graph_exec_symbol_t*)24
ccv_array_get24
(graph->sources, 0) :
00
;
756
24
}
757
758
void ccv_nnc_symbolic_graph_add_source(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t source)
759
10
{
760
10
  if (!graph->sources)
761
0
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
762
10
  assert(source.graph == graph);
763
10
  ccv_array_push(graph->sources, &source);
764
10
}
765
766
void ccv_nnc_symbolic_graph_set_sources(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t* const sources, const int source_size)
767
10
{
768
10
  if (!graph->sources)
769
6
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
770
10
  else
771
4
    ccv_array_clear(graph->sources);
772
10
  int i;
773
20
  for (i = 0; 
i < source_size20
;
i++10
)
774
10
    ccv_nnc_symbolic_graph_add_source(graph, sources[i]);
775
10
}
776
777
int ccv_nnc_symbolic_graph_source_size(const ccv_nnc_symbolic_graph_t* const graph)
778
24
{
779
24
  return graph->sources ? 
graph->sources->rnum24
:
00
;
780
24
}
781
782
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_destinations(const ccv_nnc_symbolic_graph_t* const graph)
783
30
{
784
30
  return graph->destinations ? 
(ccv_nnc_graph_exec_symbol_t*)30
ccv_array_get30
(graph->destinations, 0) :
00
;
785
30
}
786
787
void ccv_nnc_symbolic_graph_add_destination(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t destination)
788
16
{
789
16
  if (!graph->destinations)
790
0
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
791
16
  assert(destination.graph == graph);
792
16
  ccv_array_push(graph->destinations, &destination);
793
16
}
794
795
void ccv_nnc_symbolic_graph_set_destinations(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t* const destinations, const int destination_size)
796
10
{
797
10
  if (!graph->destinations)
798
6
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
799
10
  else
800
4
    ccv_array_clear(graph->destinations);
801
10
  int i;
802
20
  for (i = 0; 
i < destination_size20
;
i++10
)
803
10
    ccv_nnc_symbolic_graph_add_destination(graph, destinations[i]);
804
10
}
805
806
int ccv_nnc_symbolic_graph_destination_size(const ccv_nnc_symbolic_graph_t* const graph)
807
30
{
808
30
  return graph->destinations ? 
graph->destinations->rnum30
:
00
;
809
30
}
810
811
static void _ccv_nnc_symbolic_graph_dot_exec_symbol(const int index, const ccv_nnc_graph_exec_symbol_info_t* const symbol_info, const int flags, FILE* out)
812
178
{
813
178
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
814
151
    fputc('{', out);
815
178
  if (symbol_info->name)
816
81
    fputs(symbol_info->name, out);
817
178
  else
818
97
    fprintf(out, "node%d", index);
819
178
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
820
151
  {
821
151
    fputs("|Command: ", out);
822
151
    fputs(ccv_nnc_cmd_name(symbol_info->cmd.cmd), out);
823
151
    fputc('}', out);
824
151
  }
825
178
}
826
827
static void _ccv_nnc_symbolic_graph_dot_tensor_symbol(const int index, const ccv_nnc_tensor_symbol_info_t* const symbol_info, const ccv_nnc_tensor_symbol_info_t* const alias_info, const int html_like, const int flags, FILE* out)
828
567
{
829
567
  // if it has an alias pointer, or, it is a long form.
830
567
  if (
(flags == CCV_NNC_LONG_DOT_GRAPH || 567
alias_info86
) &&
!html_like513
)
831
493
    fputc('{', out);
832
567
  if (symbol_info->name)
833
298
    fputs(symbol_info->name, out);
834
567
  else
835
269
    fprintf(out, "tensor%d", index);
836
567
  if (
flags == CCV_NNC_LONG_DOT_GRAPH && 567
(symbol_info->flags & CCV_NNC_SYM_TENSOR_INIT_ZEROS)481
)
837
7
    fputs(" (0)", out); // Output if it is zero init'ed.
838
567
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
839
481
  {
840
481
    int i;
841
481
    if (html_like)
842
20
      fprintf(out, "</td><td>%d", symbol_info->info.dim[0]);
843
481
    else
844
461
      fprintf(out, "|%d", symbol_info->info.dim[0]);
845
822
    for (i = 1; 
i < 822
CCV_NNC_MAX_DIM_ALLOC822
&&
symbol_info->info.dim[i]822
;
i++341
)
846
341
      fprintf(out, "x%d", symbol_info->info.dim[i]);
847
481
  }
848
567
  if (alias_info)
849
140
  {
850
140
    if (html_like)
851
0
      fputs("</td><td border=\"0\">as. ", out);
852
140
    else
853
140
      fputs("|as. ", out);
854
140
    if (alias_info->name)
855
89
      fputs(alias_info->name, out);
856
140
    else
857
51
      fprintf(out, "tensor%d", symbol_info->alias_ref - 1);
858
140
    if (
flags == CCV_NNC_LONG_DOT_GRAPH && 140
(alias_info->flags & CCV_NNC_SYM_TENSOR_INIT_ZEROS)108
)
859
7
      fputs(" (0)", out); // Output if it is zero init'ed.
860
140
  }
861
567
  if (
(flags == CCV_NNC_LONG_DOT_GRAPH || 567
alias_info86
) &&
!html_like513
)
862
493
    fputc('}', out);
863
567
}
864
865
static void _ccv_nnc_symbolic_graph_dot_node(const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int index, const ccv_array_t* const tensor_symbol_info, const int flags, FILE* out)
866
178
{
867
178
  fprintf(out, "node%d [shape=record,label=\"", index);
868
178
  _ccv_nnc_symbolic_graph_dot_exec_symbol(index, exec_symbol_info, flags, out);
869
178
  int i;
870
178
  if (exec_symbol_info->input_size > 0)
871
164
  {
872
164
    fputs("|{Input", out);
873
569
    for (i = 0; 
i < exec_symbol_info->input_size569
;
i++405
)
874
405
    {
875
405
      if (exec_symbol_info->inputs[i] >= 0)
876
351
      {
877
351
        fputc('|', out);
878
351
        const ccv_nnc_tensor_symbol_info_t* const tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(tensor_symbol_info, exec_symbol_info->inputs[i]);
879
263
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)88
ccv_array_get88
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
0263
;
880
351
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 0, flags, out);
881
351
      } else
882
54
        fputs("|-", out);
883
405
    }
884
164
    fputc('}', out);
885
164
  }
886
178
  if (exec_symbol_info->output_size > 0)
887
164
  {
888
164
    fputs("|{Output", out);
889
363
    for (i = 0; 
i < exec_symbol_info->output_size363
;
i++199
)
890
199
    {
891
199
      if (exec_symbol_info->outputs[i] >= 0)
892
196
      {
893
196
        fputc('|', out);
894
196
        const ccv_nnc_tensor_symbol_info_t* const tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(tensor_symbol_info, exec_symbol_info->outputs[i]);
895
144
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)52
ccv_array_get52
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
0144
;
896
196
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 0, flags, out);
897
196
      } else
898
3
        fputs("|-", out);
899
199
    }
900
164
    fputc('}', out);
901
164
  }
902
178
  fputs("\"];\n", out);
903
178
}
904
905
static void _ccv_nnc_symbolic_graph_dot_while_label(const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int index, const ccv_array_t* const tensor_symbol_info, const ccv_nnc_symbolic_graph_t* const while_graph, const int flags, FILE* out)
906
12
{
907
12
  int i;
908
12
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
909
12
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"3\" border=\"0\"><b>", out);
910
12
  else
911
0
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"2\" border=\"0\"><b>", out);
912
12
  if (exec_symbol_info->name)
913
12
    fputs(exec_symbol_info->name, out);
914
12
  else
915
0
    fprintf(out, "while%d", index);
916
12
  fputs("</b></td></tr>", out);
917
12
  const int p_idx = while_graph->p_idx - 1;
918
12
  assert(p_idx >= 0);
919
12
  if (exec_symbol_info->input_size > 0)
920
10
  {
921
10
    fprintf(out, "<tr><td rowspan=\"%d\">Input</td>", exec_symbol_info->input_size);
922
23
    for (i = 0; 
i < exec_symbol_info->input_size23
;
i++13
)
923
13
    {
924
13
      if (i > 0)
925
3
        fputs("<tr>", out);
926
13
      if (exec_symbol_info->inputs[i] >= 0)
927
13
      {
928
13
        fputs("<td>", out);
929
13
        const ccv_nnc_tensor_symbol_info_t* const tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(tensor_symbol_info, exec_symbol_info->inputs[i]);
930
13
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)0
ccv_array_get0
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
013
;
931
13
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 1, flags, out);
932
13
        fputs("</td><td border=\"0\">=&gt; ", out);
933
13
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
934
13
        assert(s_idx >= 0);
935
13
        const ccv_nnc_tensor_symbol_info_t* const sub_tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(while_graph->tensor_symbol_info, s_idx);
936
13
        if (sub_tensor_symbol->name)
937
12
          fputs(sub_tensor_symbol->name, out);
938
13
        else
939
1
          fprintf(out, "tensor%d", s_idx);
940
13
        fputs("</td></tr>", out);
941
0
      } else {
942
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
943
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
944
0
        else
945
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
946
0
      }
947
13
    }
948
10
  }
949
12
  if (exec_symbol_info->output_size > 0)
950
7
  {
951
7
    fprintf(out, "<tr><td rowspan=\"%d\">Output</td>", exec_symbol_info->output_size);
952
14
    for (i = 0; 
i < exec_symbol_info->output_size14
;
i++7
)
953
7
    {
954
7
      if (i > 0)
955
0
        fputs("<tr>", out);
956
7
      if (exec_symbol_info->outputs[i] >= 0)
957
7
      {
958
7
        fputs("<td>", out);
959
7
        ccv_nnc_tensor_symbol_info_t* tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(tensor_symbol_info, exec_symbol_info->outputs[i]);
960
7
        ccv_nnc_tensor_symbol_info_t* alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)0
ccv_array_get0
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
07
;
961
7
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 1, flags, out);
962
7
        fputs("</td><td border=\"0\">=&gt; ", out);
963
7
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
964
7
        assert(s_idx >= 0);
965
7
        const ccv_nnc_tensor_symbol_info_t* const sub_tensor_symbol = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(while_graph->tensor_symbol_info, s_idx);
966
7
        if (sub_tensor_symbol->name)
967
6
          fputs(sub_tensor_symbol->name, out);
968
7
        else
969
1
          fprintf(out, "tensor%d", s_idx);
970
7
        fputs("</td></tr>", out);
971
0
      } else {
972
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
973
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
974
0
        else
975
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
976
0
      }
977
7
    }
978
7
  }
979
70
  for (i = 0; 
i < while_graph->tensor_symbol_info->rnum70
;
i++58
)
980
58
  {
981
58
    const ccv_nnc_tensor_symbol_info_t* const tensor_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(while_graph->tensor_symbol_info, i);
982
58
    if (tensor_symbol_info->assign_ref)
983
13
    {
984
13
      if (flags == CCV_NNC_LONG_DOT_GRAPH)
985
13
        fputs("<tr><td colspan=\"3\" border=\"0\">", out);
986
13
      else
987
0
        fputs("<tr><td colspan=\"2\" border=\"0\">", out);
988
13
      const ccv_nnc_tensor_symbol_info_t* const assign_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(while_graph->tensor_symbol_info, tensor_symbol_info->assign_ref - 1);
989
13
      if (assign_symbol_info->name)
990
11
        fputs(assign_symbol_info->name, out);
991
13
      else
992
2
        fprintf(out, "tensor%d", tensor_symbol_info->assign_ref - 1);
993
13
      fputs(" -&gt; ", out);
994
13
      if (tensor_symbol_info->name)
995
11
        fputs(tensor_symbol_info->name, out);
996
13
      else
997
2
        fprintf(out, "tensor%d", i);
998
13
      fputs("</td></tr>", out);
999
13
    }
1000
58
  }
1001
12
  fputs("</table>", out);
1002
12
}
1003
1004
static void _ccv_nnc_symbolic_graph_dot_sub_graph(const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const ccv_array_t* const tensor_symbol_info, const ccv_nnc_symbolic_graph_t* const while_graph, const int flags, FILE* out, int* c)
1005
12
{
1006
12
  fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=<", *c, *c);
1007
12
  int i, j;
1008
12
  // Output this node info within this subgraph.
1009
12
  _ccv_nnc_symbolic_graph_dot_while_label(exec_symbol_info, *c, tensor_symbol_info, while_graph, flags, out);
1010
12
  fputs(">;\n", out);
1011
12
  ++(*c);
1012
12
  int* node_id = (int*)ccmalloc(sizeof(int) * while_graph->exec_symbol_info->rnum);
1013
40
  for (i = 0; 
i < while_graph->exec_symbol_info->rnum40
;
i++28
)
1014
28
  {
1015
28
    node_id[i] = *c;
1016
28
    const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(while_graph->exec_symbol_info, i);
1017
28
    // Skip the dead one.
1018
28
    if (exec_symbol_info->dead)
1019
0
      continue;
1020
28
    
if (28
exec_symbol_info->graph_ref28
)
1021
1
    {
1022
1
      const ccv_nnc_symbolic_graph_t* const graph = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(while_graph->sub_graphs, exec_symbol_info->graph_ref - 1);
1023
1
      _ccv_nnc_symbolic_graph_dot_sub_graph(exec_symbol_info, while_graph->tensor_symbol_info, graph, flags, out, c);
1024
27
    } else {
1025
27
      _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, *c, while_graph->tensor_symbol_info, flags, out);
1026
27
      ++(*c);
1027
27
    }
1028
28
  }
1029
12
  // Output connections.
1030
40
  for (i = 0; 
i < while_graph->exec_symbol_info->rnum40
;
i++28
)
1031
28
  {
1032
28
    const ccv_nnc_graph_exec_symbol_info_t* exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(while_graph->exec_symbol_info, i);
1033
28
    // Skip the dead one.
1034
28
    if (exec_symbol_info->dead)
1035
0
      continue;
1036
28
    
if (28
exec_symbol_info->outgoings28
)
1037
33
      
for (j = 0; 16
j < exec_symbol_info->outgoings->rnum33
;
j++17
)
1038
17
      {
1039
17
        const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1040
17
        const ccv_nnc_graph_exec_symbol_info_t* const outgoing_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(while_graph->exec_symbol_info, outgoing_idx);
1041
17
        // If both are sub-graphs, have both tail and head specified.
1042
17
        if (
exec_symbol_info->graph_ref && 17
outgoing_symbol_info->graph_ref1
)
1043
0
          fprintf(out, "node%d -> node%d [ltail=cluster%d,lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i], node_id[outgoing_idx]);
1044
17
        else 
if (17
exec_symbol_info->graph_ref && 17
!outgoing_symbol_info->graph_ref1
)
1045
1
          fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1046
16
        else 
if (16
!exec_symbol_info->graph_ref && 16
outgoing_symbol_info->graph_ref16
)
1047
1
          fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1048
16
        else
1049
15
          fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1050
17
      }
1051
28
  }
1052
12
  fputs("}\n", out);
1053
12
  ccfree(node_id);
1054
12
}
1055
1056
void ccv_nnc_symbolic_graph_dot(const ccv_nnc_symbolic_graph_t* const graph, const int flags, FILE* out)
1057
24
{
1058
24
  fputs("digraph G {\ncompound=true;\n", out);
1059
24
  int i, j;
1060
24
  int c = 0;
1061
24
  int* node_id = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
1062
24
  // Output styles.
1063
186
  for (i = 0; 
i < graph->exec_symbol_info->rnum186
;
i++162
)
1064
162
  {
1065
162
    node_id[i] = c;
1066
162
    const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1067
162
    // Skip the dead one.
1068
162
    if (exec_symbol_info->dead)
1069
0
      continue;
1070
162
    
if (162
exec_symbol_info->graph_ref162
)
1071
11
    {
1072
11
      const ccv_nnc_symbolic_graph_t* const while_graph = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(graph->sub_graphs, exec_symbol_info->graph_ref - 1);
1073
11
      _ccv_nnc_symbolic_graph_dot_sub_graph(exec_symbol_info, graph->tensor_symbol_info, while_graph, flags, out, &c);
1074
151
    } else {
1075
151
      _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, c, graph->tensor_symbol_info, flags, out);
1076
151
      ++c;
1077
151
    }
1078
162
  }
1079
24
  // Output connections.
1080
186
  for (i = 0; 
i < graph->exec_symbol_info->rnum186
;
i++162
)
1081
162
  {
1082
162
    const ccv_nnc_graph_exec_symbol_info_t* exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1083
162
    // Skip the dead one.
1084
162
    if (exec_symbol_info->dead)
1085
0
      continue;
1086
162
    
if (162
exec_symbol_info->outgoings162
)
1087
341
      
for (j = 0; 135
j < exec_symbol_info->outgoings->rnum341
;
j++206
)
1088
206
      {
1089
206
        const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1090
206
        const ccv_nnc_graph_exec_symbol_info_t* const outgoing_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, outgoing_idx);
1091
206
        // If both are sub-graphs, have both tail and head specified.
1092
206
        if (
exec_symbol_info->graph_ref && 206
outgoing_symbol_info->graph_ref6
)
1093
1
          fprintf(out, "node%d -> node%d [ltail=cluster%d,lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i], node_id[outgoing_idx]);
1094
205
        else 
if (205
exec_symbol_info->graph_ref && 205
!outgoing_symbol_info->graph_ref5
)
1095
5
          fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1096
200
        else 
if (200
!exec_symbol_info->graph_ref && 200
outgoing_symbol_info->graph_ref200
)
1097
1
          fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1098
200
        else
1099
199
          fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1100
206
      }
1101
162
  }
1102
24
  fputs("}\n", out);
1103
24
  ccfree(node_id);
1104
24
}
1105
1106
void ccv_nnc_symbolic_graph_free(ccv_nnc_symbolic_graph_t* const graph)
1107
41
{
1108
41
  int i;
1109
258
  for (i = 0; 
i < graph->exec_symbol_info->rnum258
;
i++217
)
1110
217
  {
1111
217
    ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1112
217
    if (symbol_info->name)
1113
105
      
ccfree105
(symbol_info->name)105
;
1114
217
    ccv_array_t* outgoings = symbol_info->outgoings;
1115
217
    if (outgoings)
1116
173
      ccv_array_free(outgoings);
1117
217
    // We allocate inputs & outputs in continuous fashion, therefore, only need to free the input array.
1118
217
    if (symbol_info->inputs)
1119
190
      
ccfree190
(symbol_info->inputs)190
;
1120
217
  }
1121
507
  for (i = 0; 
i < graph->tensor_symbol_info->rnum507
;
i++466
)
1122
466
  {
1123
466
    ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i);
1124
466
    if (symbol_info->name)
1125
234
      
ccfree234
(symbol_info->name)234
;
1126
466
    if (symbol_info->s_ref)
1127
22
      ccv_array_free(symbol_info->s_ref);
1128
466
  }
1129
41
  if (graph->sub_graphs)
1130
10
  {
1131
22
    for (i = 0; 
i < graph->sub_graphs->rnum22
;
i++12
)
1132
12
      
ccv_nnc_symbolic_graph_free(*(ccv_nnc_symbolic_graph_t**)12
ccv_array_get12
(graph->sub_graphs, i));
1133
10
    ccv_array_free(graph->sub_graphs);
1134
10
  }
1135
41
  if (graph->sources)
1136
27
    ccv_array_free(graph->sources);
1137
41
  if (graph->destinations)
1138
27
    ccv_array_free(graph->destinations);
1139
41
  if (graph->breakpoints)
1140
16
    
ccfree16
(graph->breakpoints)16
;
1141
41
  ccv_array_free(graph->tensor_symbol_info);
1142
41
  ccv_array_free(graph->exec_symbol_info);
1143
41
  if (graph->backward_tensor_symbols)
1144
13
    
ccfree13
(graph->backward_tensor_symbols)13
;
1145
41
  ccfree(graph);
1146
41
}
1147
1148
void ccv_nnc_symbolic_graph_symbol_infer(const ccv_nnc_symbolic_graph_t* const symbolic_graph, const ccv_nnc_graph_visit_t* const visit, const ccv_nnc_graph_exec_symbol_t* const sources, const int source_size, const ccv_nnc_graph_exec_symbol_t* const destinations, const int destination_size, const ccv_nnc_tensor_symbol_info_t* const p_tensor_symbol_info, const int p_tensor_symbol_info_size, ccv_nnc_tensor_symbol_info_t* const tensor_symbol_info, ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info)
1149
47
{
1150
47
  memcpy(tensor_symbol_info, ccv_array_get(symbolic_graph->tensor_symbol_info, 0), sizeof(ccv_nnc_tensor_symbol_info_t) * symbolic_graph->tensor_symbol_info->rnum);
1151
47
  memcpy(exec_symbol_info, ccv_array_get(symbolic_graph->exec_symbol_info, 0), sizeof(ccv_nnc_graph_exec_symbol_info_t) * symbolic_graph->exec_symbol_info->rnum);
1152
47
  int i;
1153
47
  if (p_tensor_symbol_info)
1154
111
    
for (i = 0; 15
i < symbolic_graph->tensor_symbol_info->rnum111
;
i++96
)
1155
96
      
if (96
tensor_symbol_info[i].p_ref96
)
1156
24
      {
1157
24
        const int p_ref = tensor_symbol_info[i].p_ref - 1;
1158
24
        assert(p_ref < p_tensor_symbol_info_size);
1159
24
        tensor_symbol_info[i].info = p_tensor_symbol_info[p_ref].info;
1160
24
        // I don't need to copy over inc and ofs for alias.
1161
24
      }
1162
47
  int max_input_size = 0, max_output_size = 0;
1163
47
  // Materialize auto hints.
1164
260
  for (i = 0; 
i < symbolic_graph->exec_symbol_info->rnum260
;
i++213
)
1165
213
  {
1166
213
    max_input_size = ccv_max(max_input_size, exec_symbol_info[i].input_size);
1167
213
    max_output_size = ccv_max(max_output_size, exec_symbol_info[i].output_size);
1168
213
    // If there is no hint and we have input and output tensor specified.
1169
213
    if (ccv_nnc_is_no_hint(exec_symbol_info[i].hint) &&
1170
213
      
exec_symbol_info[i].input_size > 0213
&&
exec_symbol_info[i].inputs[0] >= 0189
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].inputs[0]].info)189
&&
1171
189
      
exec_symbol_info[i].output_size > 0189
&&
exec_symbol_info[i].outputs[0] >= 0186
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].outputs[0]].info)186
)
1172
186
      exec_symbol_info[i].hint = ccv_nnc_hint_auto(exec_symbol_info[i].cmd.info, tensor_symbol_info[exec_symbol_info[i].inputs[0]].info, tensor_symbol_info[exec_symbol_info[i].outputs[0]].info);
1173
213
  }
1174
47
1175
46
  ccv_nnc_tensor_param_t* input_params = max_input_size > 0 ? 
(ccv_nnc_tensor_param_t*)46
ccmalloc46
(sizeof(ccv_nnc_tensor_param_t) * max_input_size) :
01
;
1176
43
  ccv_nnc_tensor_param_t* output_params = max_output_size > 0 ? 
(ccv_nnc_tensor_param_t*)43
ccmalloc43
(sizeof(ccv_nnc_tensor_param_t) * max_output_size) :
04
;
1177
47
1178
47
  // Materialize auto tensors. This need to go with the topological order.
1179
47
  // TODO: Need to proper handle sub-graphs (thus, run sub-graph to figure out the tensor properties).
1180
212
  
ccv_nnc_graph_visit_for212
(visit, exec_symbol_info, node) {212
1181
212
    if (
node->input_size > 0 && 212
node->output_size > 0189
)
1182
186
    {
1183
594
      for (i = 0; 
i < node->input_size594
;
i++408
)
1184
408
        
input_params[i] = node->inputs[i] >= 0 ? 408
tensor_symbol_info[node->inputs[i]].info375
:
ccv_nnc_tensor_auto33
;
1185
186
      ccv_nnc_hint_tensor_auto(node->cmd, input_params, node->input_size, node->hint, output_params, node->output_size);
1186
391
      for (i = 0; 
i < node->output_size391
;
i++205
)
1187
186
        /* Only assign the output parameters if the symbol itself is auto. */
1188
205
        
if (205
node->outputs[i] >= 0 && 205
ccv_nnc_is_tensor_auto(tensor_symbol_info[node->outputs[i]].info)202
)
1189
0
          tensor_symbol_info[node->outputs[i]].info = output_params[i];
1190
186
    }
1191
212
  } ccv_nnc_graph_visit_endfor
1192
47
  if (input_params)
1193
46
    
ccfree46
(input_params)46
;
1194
47
  if (output_params)
1195
43
    
ccfree43
(output_params)43
;
1196
47
}