Coverage Report

Created: 2019-07-03 22:50

/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
#include "_ccv_nnc_symbolic_graph.h"
6
7
#pragma mark - Level-3 API
8
9
const ccv_nnc_tensor_param_t ccv_nnc_tensor_auto = {};
10
11
int ccv_nnc_is_tensor_auto(const ccv_nnc_tensor_param_t params)
12
99.2k
{
13
99.2k
  return (memcmp(&params, &ccv_nnc_tensor_auto, sizeof(ccv_nnc_tensor_param_t)) == 0);
14
99.2k
}
15
16
ccv_nnc_symbolic_graph_t* ccv_nnc_symbolic_graph_new(void)
17
185
{
18
185
  ccv_nnc_symbolic_graph_t* graph = cccalloc(1, sizeof(ccv_nnc_symbolic_graph_t));
19
185
  graph->tensor_symbol_info = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_info_t), 5, 0);
20
185
  graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), 5, 0);
21
185
  graph->reuse.exec = -1;
22
185
  graph->reuse.tensor = -1;
23
185
  return graph;
24
185
}
25
26
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)
27
13
{
28
13
  ccv_nnc_symbolic_graph_t* new_graph = ccmalloc(sizeof(ccv_nnc_symbolic_graph_t));
29
13
  memcpy(new_graph, graph, sizeof(ccv_nnc_symbolic_graph_t));
30
13
  new_graph->tensor_symbol_info = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_info_t), graph->tensor_symbol_info->rnum, 0);
31
13
  new_graph->tensor_symbol_info->rnum = graph->tensor_symbol_info->rnum;
32
13
  memcpy(ccv_array_get(new_graph->tensor_symbol_info, 0), ccv_array_get(graph->tensor_symbol_info, 0), sizeof(ccv_nnc_tensor_symbol_info_t) * graph->tensor_symbol_info->rnum);
33
13
  int i;
34
91
  for (i = 0; i < new_graph->tensor_symbol_info->rnum; 
i++78
)
35
78
  {
36
78
    ccv_nnc_tensor_symbol_info_t* symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(new_graph->tensor_symbol_info, i);
37
78
    if (symbol_info->name)
38
70
    {
39
70
      char* const name = symbol_info->name;
40
70
      const size_t n = strnlen(name, 63) + 1;
41
70
      symbol_info->name = (char*)ccmalloc(n);
42
70
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
43
70
      strncpy(symbol_info->name, name, n);
44
70
    }
45
78
    if (symbol_info->s_ref)
46
6
    {
47
6
      ccv_array_t* const s_ref = symbol_info->s_ref;
48
6
      symbol_info->s_ref = ccv_array_new(sizeof(int), s_ref->rnum, 0);
49
6
      symbol_info->s_ref->rnum = s_ref->rnum;
50
6
      memcpy(ccv_array_get(symbol_info->s_ref, 0), ccv_array_get(s_ref, 0), sizeof(int) * s_ref->rnum);
51
6
    }
52
78
  }
53
13
  new_graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), graph->exec_symbol_info->rnum, 0);
54
13
  new_graph->exec_symbol_info->rnum = graph->exec_symbol_info->rnum;
55
13
  memcpy(ccv_array_get(new_graph->exec_symbol_info, 0), ccv_array_get(graph->exec_symbol_info, 0), sizeof(ccv_nnc_graph_exec_symbol_info_t) * graph->exec_symbol_info->rnum);
56
48
  for (i = 0; i < new_graph->exec_symbol_info->rnum; 
i++35
)
57
35
  {
58
35
    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);
59
35
    if (symbol_info->name)
60
25
    {
61
25
      char* const name = symbol_info->name;
62
25
      const size_t n = strnlen(name, 63) + 1;
63
25
      symbol_info->name = (char*)ccmalloc(n);
64
25
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
65
25
      strncpy(symbol_info->name, name, n);
66
25
    }
67
35
    if (symbol_info->outgoings)
68
20
    {
69
20
      ccv_array_t* const outgoings = symbol_info->outgoings;
70
20
      symbol_info->outgoings = ccv_array_new(sizeof(int), outgoings->rnum, 0);
71
20
      symbol_info->outgoings->rnum = outgoings->rnum;
72
20
      memcpy(ccv_array_get(symbol_info->outgoings, 0), ccv_array_get(outgoings, 0), sizeof(int) * outgoings->rnum);
73
20
    }
74
35
    if (symbol_info->inputs)
75
22
    {
76
22
      int* const inputs = symbol_info->inputs;
77
22
      symbol_info->inputs = (int*)ccmalloc(sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
78
22
      symbol_info->outputs = symbol_info->inputs + symbol_info->input_size;
79
22
      memcpy(symbol_info->inputs, inputs, sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
80
22
    }
81
35
    if (symbol_info->_heap_graph_ref)
82
2
    {
83
2
      int* const heap_graph_ref = symbol_info->_heap_graph_ref;
84
2
      symbol_info->_heap_graph_ref = (int*)ccmalloc(sizeof(int) * symbol_info->graph_ref_size);
85
2
      memcpy(symbol_info->_heap_graph_ref, heap_graph_ref, sizeof(int) * symbol_info->graph_ref_size);
86
2
    }
87
35
    if ((symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE) && 
symbol_info->input_size > 01
)
88
1
    {
89
1
      int* const inputs = symbol_info->p_while.inputs;
90
1
      symbol_info->p_while.inputs = (int*)ccmalloc(sizeof(int) * symbol_info->p_while.input_size);
91
1
      memcpy(symbol_info->p_while.inputs, inputs, sizeof(int) * symbol_info->p_while.input_size);
92
1
    }
93
35
  }
94
13
  if (graph->sources)
95
13
  {
96
13
    new_graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->sources->rnum, 0);
97
13
    new_graph->sources->rnum = graph->sources->rnum;
98
13
    memcpy(ccv_array_get(new_graph->sources, 0), ccv_array_get(graph->sources, 0), sizeof(ccv_nnc_graph_exec_symbol_t) * graph->sources->rnum);
99
26
    for (i = 0; i < new_graph->sources->rnum; 
i++13
)
100
13
      ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(new_graph->sources, i))->graph = new_graph;
101
13
  }
102
13
  if (graph->destinations)
103
13
  {
104
13
    new_graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->destinations->rnum, 0);
105
13
    new_graph->destinations->rnum = graph->destinations->rnum;
106
13
    memcpy(ccv_array_get(new_graph->destinations, 0), ccv_array_get(graph->destinations, 0), sizeof(ccv_nnc_graph_exec_symbol_t) * graph->destinations->rnum);
107
26
    for (i = 0; i < new_graph->destinations->rnum; 
i++13
)
108
13
      ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(new_graph->destinations, i))->graph = new_graph;
109
13
  }
110
13
  if (graph->breakpoints)
111
13
  {
112
13
    new_graph->breakpoints = (ccv_nnc_graph_exec_symbol_t*)ccmalloc(sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
113
13
    memcpy(new_graph->breakpoints, graph->breakpoints, sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
114
26
    for (i = 0; i < graph->breakpoint_size; 
i++13
)
115
13
      new_graph->breakpoints[i].graph = new_graph;
116
13
  }
117
13
  if (graph->backward.tensor_symbol_idx)
118
1
  {
119
1
    new_graph->backward.tensor_symbol_idx = (int*)ccmalloc(sizeof(int) * (new_graph->backward.tensor_symbol_size + new_graph->backward.exec_symbol_size));
120
1
    if (new_graph->backward.tensor_symbol_size > 0)
121
1
      memcpy(new_graph->backward.tensor_symbol_idx, graph->backward.tensor_symbol_idx, sizeof(int) * new_graph->backward.tensor_symbol_size);
122
1
    new_graph->backward.exec_symbol_idx = new_graph->backward.tensor_symbol_idx + new_graph->backward.tensor_symbol_size;
123
1
    if (new_graph->backward.exec_symbol_size > 0)
124
1
      memcpy(new_graph->backward.exec_symbol_idx, graph->backward.exec_symbol_idx, sizeof(int) * new_graph->backward.exec_symbol_size);
125
1
  }
126
13
  if (subst)
127
13
  {
128
48
    for (i = 0; i < new_graph->exec_symbol_info->rnum; 
i++35
)
129
35
    {
130
35
      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);
131
35
      if (!CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
132
35
      {
133
33
        symbol_info->cmd = subst((ccv_nnc_graph_exec_symbol_t){
134
33
          .d = i,
135
33
          .graph = graph,
136
33
        }, symbol_info->cmd);
137
33
        if (symbol_info->cmd.cmd != CCV_NNC_GRAPH_FORWARD && symbol_info->cmd.cmd != CCV_NNC_GRAPH_BACKWARD)
138
33
        {
139
33
          symbol_info->graph_ref_size = 0;
140
33
          if (symbol_info->_heap_graph_ref)
141
2
          {
142
2
            ccfree(symbol_info->_heap_graph_ref);
143
2
            symbol_info->_heap_graph_ref = 0;
144
2
          }
145
33
        }
146
33
      }
147
35
    }
148
13
  }
149
13
  // TODO: See how and if I need to dup sub-graphs. I also need to figure out what's the relationship between this graph
150
13
  // and its parent graph (or how can we use the symbol from the graph properly).
151
13
  new_graph->sub_graphs = 0;
152
13
  return new_graph;
153
13
}
154
155
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)
156
20.0k
{
157
20.0k
  ccv_nnc_tensor_symbol_t symbol = {
158
20.0k
    .d = graph->tensor_symbol_info->rnum,
159
20.0k
    .graph = graph
160
20.0k
  };
161
20.0k
  ccv_nnc_tensor_symbol_info_t symbol_info = {
162
20.0k
    .info = info,
163
20.0k
  };
164
20.0k
  if (name)
165
701
  {
166
701
    size_t n = strnlen(name, 63) + 1;
167
701
    symbol_info.name = (char*)ccmalloc(n);
168
701
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
169
701
    strncpy(symbol_info.name, name, n);
170
701
  }
171
20.0k
  if (graph->reuse.tensor >= 0)
172
6.00k
  {
173
6.00k
    const int reuse_tensor_d = graph->reuse.tensor;
174
6.00k
    assert(reuse_tensor_d < graph->tensor_symbol_info->rnum);
175
6.00k
    *(ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, reuse_tensor_d) = symbol_info;
176
6.00k
    int i;
177
6.00k
    graph->reuse.tensor = -1;
178
11.0k
    for (i = reuse_tensor_d + 1; i < graph->tensor_symbol_info->rnum && 
graph->reuse.tensor < 09.01k
;
i++5.01k
)
179
5.01k
      if (CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
180
5.01k
        
graph->reuse.tensor = i4.99k
;
181
6.00k
    symbol.d = reuse_tensor_d;
182
6.00k
  } else
183
14.0k
    ccv_array_push(graph->tensor_symbol_info, &symbol_info);
184
20.0k
  if (graph->hooks.tensor_symbol_new.func)
185
12.2k
    graph->hooks.tensor_symbol_new.func(graph->hooks.tensor_symbol_new.context, symbol, info, name);
186
20.0k
  return symbol;
187
20.0k
}
188
189
void* ccv_nnc_tensor_symbol_new_hook(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_tensor_symbol_new_hook_f hook, void* context)
190
2.02k
{
191
2.02k
  void* const prev = graph->hooks.tensor_symbol_new.context;
192
2.02k
  graph->hooks.tensor_symbol_new.func = hook;
193
2.02k
  graph->hooks.tensor_symbol_new.context = context;
194
2.02k
  return prev;
195
2.02k
}
196
197
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)
198
172
{
199
172
  assert(tensor_symbol.graph == graph);
200
172
  int d = tensor_symbol.d;
201
172
  assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
202
172
  ccv_nnc_tensor_symbol_info_t* info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
203
172
  // Find the root tensor that is not an alias.
204
172
  while (info_d->alias_ref)
205
0
  {
206
0
    d = info_d->alias_ref - 1;
207
0
    assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
208
0
    info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
209
0
  }
210
172
  ccv_nnc_tensor_symbol_t alias = {
211
172
    .d = graph->tensor_symbol_info->rnum,
212
172
    .graph = graph
213
172
  };
214
172
  int i;
215
172
  // 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.
216
1.54k
  for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC; 
i++1.37k
)
217
1.37k
    { assert(info.dim[i] + ofs[i] <= inc[i]); }
218
172
  // If it is not auto, check dimensions.
219
172
  if (!ccv_nnc_is_tensor_auto(info_d->info))
220
172
    { assert(ccv_nnc_dimension_count(inc) <= ccv_nnc_tensor_count(info_d->info)); }
221
172
  ccv_nnc_tensor_symbol_info_t alias_info = {
222
172
    .alias_ref = d + 1,
223
172
    .info = info,
224
172
  };
225
172
  if (name)
226
64
  {
227
64
    size_t n = strnlen(name, 63) + 1;
228
64
    alias_info.name = (char*)ccmalloc(n);
229
64
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
230
64
    strncpy(alias_info.name, name, n);
231
64
  }
232
172
  memcpy(alias_info.ofs, ofs, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
233
172
  memcpy(alias_info.inc, inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
234
172
  if (graph->reuse.tensor >= 0)
235
0
  {
236
0
    const int reuse_tensor_d = graph->reuse.tensor;
237
0
    *(ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, reuse_tensor_d) = alias_info;
238
0
    int i;
239
0
    graph->reuse.tensor = -1;
240
0
    for (i = reuse_tensor_d + 1; i < graph->tensor_symbol_info->rnum && graph->reuse.tensor < 0; i++)
241
0
      if (CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
242
0
        graph->reuse.tensor = i;
243
0
    alias.d = reuse_tensor_d;
244
0
  } else
245
172
    ccv_array_push(graph->tensor_symbol_info, &alias_info);
246
172
  if (graph->hooks.tensor_symbol_alias_new.func)
247
23
    graph->hooks.tensor_symbol_alias_new.func(graph->hooks.tensor_symbol_alias_new.context, alias, tensor_symbol, ofs, inc, info, name);
248
172
  return alias;
249
172
}
250
251
void* ccv_nnc_tensor_symbol_alias_new_hook(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_tensor_symbol_alias_new_hook_f hook, void* context)
252
2.02k
{
253
2.02k
  void* const prev = graph->hooks.tensor_symbol_alias_new.context;
254
2.02k
  graph->hooks.tensor_symbol_alias_new.func = hook;
255
2.02k
  graph->hooks.tensor_symbol_alias_new.context = context;
256
2.02k
  return prev;
257
2.02k
}
258
259
ccv_nnc_tensor_symbol_t ccv_nnc_tensor_symbol_alias_to(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_symbol)
260
10.2k
{
261
10.2k
  assert(tensor_symbol.graph == graph);
262
10.2k
  int d = tensor_symbol.d;
263
10.2k
  assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
264
10.2k
  ccv_nnc_tensor_symbol_info_t* info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
265
10.2k
  // Find the root tensor that is not an alias.
266
10.2k
  while (info_d->alias_ref)
267
1
  {
268
1
    d = info_d->alias_ref - 1;
269
1
    assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
270
1
    info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
271
1
  }
272
10.2k
  if (d != tensor_symbol.d)
273
1
    return (ccv_nnc_tensor_symbol_t){
274
1
      .d = d,
275
1
      .graph = graph
276
1
    };
277
10.2k
  return (ccv_nnc_tensor_symbol_t){
278
10.2k
    .d = CCV_NNC_NO_TENSOR_SYMBOL,
279
10.2k
    .graph = 0
280
10.2k
  };
281
10.2k
}
282
283
// Resolve this tensor symbol to the current graph. If cannot find, return no symbol.
284
ccv_nnc_tensor_symbol_t ccv_nnc_tensor_symbol_resolve(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_symbol)
285
19.8k
{
286
19.8k
  if (graph == tensor_symbol.graph)
287
19.8k
    return tensor_symbol;
288
34
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(tensor_symbol.graph->tensor_symbol_info, tensor_symbol.d);
289
34
  assert(!symbol_info->alias_ref);
290
34
  // Find if the symbol is in the sub-graph.
291
34
  const ccv_nnc_symbolic_graph_t* curr_graph = tensor_symbol.graph;
292
34
  assert(tensor_symbol.d >= 0 && tensor_symbol.d < curr_graph->tensor_symbol_info->rnum);
293
70
  
while (34
curr_graph &&
curr_graph != graph49
)
294
36
    curr_graph = curr_graph->p;
295
34
  if (curr_graph)
296
13
  {
297
13
    // The graph is a parent of the symbol passed in.
298
13
    curr_graph = tensor_symbol.graph;
299
13
    ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
300
13
    ccv_nnc_tensor_symbol_t curr_symbol = tensor_symbol;
301
18
    while (curr_graph != graph)
302
13
    {
303
13
      ccv_nnc_symbolic_graph_t* const p = curr_graph->p;
304
13
      // Cannot find the relevant one in the parent graph, return no symbol.
305
13
      if (!curr_symbol_info->p_ref)
306
8
        return (ccv_nnc_tensor_symbol_t){
307
8
          .d = CCV_NNC_NO_TENSOR_SYMBOL,
308
8
          .graph = graph,
309
8
        };
310
5
      curr_symbol.d = curr_symbol_info->p_ref - 1;
311
5
      curr_symbol.graph = p;
312
5
      assert(curr_symbol.d >= 0 && curr_symbol.d < p->tensor_symbol_info->rnum);
313
5
      curr_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, curr_symbol.d);
314
5
      // Move on.
315
5
      curr_graph = p;
316
5
    }
317
13
    
return curr_symbol5
;
318
21
  }
319
21
  // Otherwise, if the symbol is in the parent graph, this is a bit more expensive because I need to keep a trace stack.
320
21
  curr_graph = graph;
321
21
  int d;
322
42
  for (d = 0; curr_graph && curr_graph != tensor_symbol.graph; 
d++21
)
323
21
    curr_graph = curr_graph->p;
324
21
  curr_graph = graph;
325
21
  assert(d > 0);
326
21
  int trace[d];
327
42
  for (d = 0; curr_graph && curr_graph != tensor_symbol.graph; 
d++21
)
328
21
  {
329
21
    const int p_idx = curr_graph->p_idx - 1;
330
21
    trace[d] = p_idx;
331
21
    curr_graph = curr_graph->p;
332
21
  }
333
21
  // If it is not in both the parent graph and the sub-graph, the input is invalid.
334
21
  assert(curr_graph);
335
21
  curr_graph = tensor_symbol.graph;
336
21
  ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
337
21
  ccv_nnc_tensor_symbol_t curr_symbol = tensor_symbol;
338
21
  // The graph is a sub graph of the symbol passed in.
339
21
  int i;
340
42
  for (i = d - 1; i >= 0; 
i--21
)
341
21
  {
342
21
    const int p_idx = trace[i];
343
21
    assert(p_idx >= 0);
344
21
    // Cannot find the relevant one in the sub-graph, return no symbol.
345
21
    if (!curr_graph->sub_graphs || !curr_symbol_info->s_ref ||
346
21
      curr_symbol_info->s_ref->rnum != curr_graph->sub_graphs->rnum)
347
0
        return (ccv_nnc_tensor_symbol_t){
348
0
          .d = CCV_NNC_NO_TENSOR_SYMBOL,
349
0
          .graph = graph,
350
0
        };
351
21
    assert(p_idx >= 0 && p_idx < curr_symbol_info->s_ref->rnum);
352
21
    const int s_idx = *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx);
353
21
    ccv_nnc_symbolic_graph_t* const s = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(curr_graph->sub_graphs, p_idx);
354
21
    curr_symbol.d = s_idx - 1;
355
21
    curr_symbol.graph = s;
356
21
    assert(curr_symbol.d >= 0 && curr_symbol.d < s->tensor_symbol_info->rnum);
357
21
    curr_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, curr_symbol.d);
358
21
    // Move on.
359
21
    curr_graph = s;
360
21
  }
361
21
  return curr_symbol;
362
21
}
363
364
// This method generate tensor symbols and their links along the way when traverse the graph.
365
enum {
366
  MAP_TENSOR_USE_AS_INPUT,
367
  MAP_TENSOR_USE_AS_OUTPUT,
368
};
369
370
static void _ccv_nnc_graph_exec_add_input_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
371
52
{
372
52
  int i;
373
83
  for (i = 0; i < exec_symbol_info->input_size; 
i++31
)
374
53
    if (exec_symbol_info->inputs[i] == d)
375
22
      return; // No need to continue, this symbol already exists as input.
376
52
  // Expand the array.
377
52
  
if (30
!exec_symbol_info->input_size30
&&
!exec_symbol_info->output_size16
)
378
16
  {
379
16
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
380
16
    exec_symbol_info->inputs[0] = d;
381
16
    exec_symbol_info->input_size = 1;
382
16
    exec_symbol_info->outputs = exec_symbol_info->inputs + 1;
383
16
    return;
384
16
  }
385
14
  exec_symbol_info->inputs = (int*)ccrealloc(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + 1 + exec_symbol_info->output_size));
386
14
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
387
14
  if (exec_symbol_info->output_size)
388
6
    memmove(exec_symbol_info->outputs + 1, exec_symbol_info->outputs, sizeof(int) * exec_symbol_info->output_size); 
389
14
  exec_symbol_info->inputs[exec_symbol_info->input_size] = d;
390
14
  ++exec_symbol_info->input_size;
391
14
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
392
14
}
393
394
static void _ccv_nnc_graph_exec_add_output_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
395
52
{
396
52
  int i;
397
69
  for (i = 0; i < exec_symbol_info->output_size; 
i++17
)
398
45
    if (exec_symbol_info->outputs[i] == d)
399
28
      return; // No need to continue, this symbol already exists as output.
400
52
  // Expand the array.
401
52
  
if (24
!exec_symbol_info->input_size24
&&
!exec_symbol_info->output_size7
)
402
3
  {
403
3
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
404
3
    exec_symbol_info->outputs = exec_symbol_info->inputs;
405
3
    exec_symbol_info->outputs[0] = d;
406
3
    exec_symbol_info->output_size = 1;
407
3
    return;
408
3
  }
409
21
  exec_symbol_info->inputs = (int*)ccrealloc(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + exec_symbol_info->output_size + 1));
410
21
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
411
21
  exec_symbol_info->outputs[exec_symbol_info->output_size] = d;
412
21
  ++exec_symbol_info->output_size;
413
21
}
414
415
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)
416
11
{
417
11
  assert(tensor_symbol.graph == graph);
418
11
  assert(tensor_symbol.d >= 0);
419
11
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
420
11
  assert(peer_tensor_symbol.graph == graph->peer);
421
11
  assert(peer_tensor_symbol.d >= 0);
422
11
  assert(peer_tensor_symbol.d < graph->peer->tensor_symbol_info->rnum);
423
11
  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);
424
11
  tensor_info->peer_ref = peer_tensor_symbol.d + 1;
425
11
}
426
427
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)
428
68
{
429
68
  assert(graph && symbol.graph);
430
68
  assert(symbol.graph != graph);
431
68
  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);
432
68
  assert(!symbol_info->alias_ref);
433
68
  // Find if the symbol is in the sub-graph.
434
68
  const ccv_nnc_symbolic_graph_t* curr_graph = symbol.graph;
435
68
  assert(symbol.d >= 0 && symbol.d < curr_graph->tensor_symbol_info->rnum);
436
139
  
while (68
curr_graph &&
curr_graph != graph90
)
437
71
    curr_graph = curr_graph->p;
438
68
  if (curr_graph)
439
19
  {
440
19
    // The graph is a parent of the symbol passed in. For this case, if we are connecting this symbol to an exec as input,
441
19
    // that means it must be an output in these sub-graphs. Otherwise, if we are connecting this symbol to an exec as output,
442
19
    // it must be an input in these sub-graphs.
443
19
    curr_graph = symbol.graph;
444
19
    ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
445
19
    ccv_nnc_tensor_symbol_t curr_symbol = symbol;
446
38
    while (curr_graph != graph)
447
19
    {
448
19
      ccv_nnc_symbolic_graph_t* const p = curr_graph->p;
449
19
      // I need to find the symbol whether it exists or not before creating new one.
450
19
      ccv_nnc_tensor_symbol_t new_symbol;
451
19
      ccv_nnc_tensor_symbol_info_t* new_symbol_info;
452
19
      if (!curr_symbol_info->p_ref)
453
18
      {
454
18
        new_symbol = ccv_nnc_tensor_symbol_new(p, curr_symbol_info->info, curr_symbol_info->name);
455
18
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
456
18
        curr_symbol_info->p_ref = new_symbol.d + 1;
457
18
        new_symbol_info->s_ref = ccv_array_new(sizeof(int), p->sub_graphs->rnum, 0);
458
18
        new_symbol_info->s_ref->rnum = p->sub_graphs->rnum;
459
18
        ccv_array_zero(new_symbol_info->s_ref);
460
18
        *(int*)ccv_array_get(new_symbol_info->s_ref, curr_graph->p_idx - 1) = curr_symbol.d + 1;
461
18
      } else {
462
1
        new_symbol.d = curr_symbol_info->p_ref - 1;
463
1
        new_symbol.graph = p;
464
1
        assert(new_symbol.d >= 0 && new_symbol.d < p->tensor_symbol_info->rnum);
465
1
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
466
1
      }
467
19
      if (curr_graph->exec_idx)
468
19
      {
469
19
        // This is a sub-graph.
470
19
        assert(p);
471
19
        assert(curr_graph->exec_idx > 0 && curr_graph->exec_idx <= p->exec_symbol_info->rnum);
472
19
        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);
473
19
        switch (map_use)
474
19
        {
475
19
          case MAP_TENSOR_USE_AS_INPUT:
476
19
            _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, new_symbol.d);
477
19
            break;
478
19
          case MAP_TENSOR_USE_AS_OUTPUT:
479
0
            _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, new_symbol.d);
480
0
            break;
481
19
        }
482
19
      }
483
19
      // Move on.
484
19
      curr_symbol = new_symbol;
485
19
      curr_symbol_info = new_symbol_info;
486
19
      curr_graph = p;
487
19
    }
488
19
    return curr_symbol.d;
489
49
  }
490
49
  // Otherwise, if the symbol is in the parent graph, this is a bit more expensive because I need to keep a trace stack.
491
49
  curr_graph = graph;
492
49
  int d;
493
99
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++50
)
494
50
    curr_graph = curr_graph->p;
495
49
  curr_graph = graph;
496
49
  assert(d > 0);
497
49
  int trace[d];
498
99
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++50
)
499
50
  {
500
50
    const int p_idx = curr_graph->p_idx - 1;
501
50
    trace[d] = p_idx;
502
50
    curr_graph = curr_graph->p;
503
50
  }
504
49
  // If it is not in both the parent graph and the sub-graph, the input is invalid.
505
49
  assert(curr_graph);
506
49
  curr_graph = symbol.graph;
507
49
  ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
508
49
  ccv_nnc_tensor_symbol_t curr_symbol = symbol;
509
49
  // 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,
510
49
  // that means it must be an input in these parent graphs. Otherwise, if we are connecting this symbol to an exec as output,
511
49
  // it must be an output in these parent graphs.
512
49
  int i;
513
99
  for (i = d - 1; i >= 0; 
i--50
)
514
50
  {
515
50
    const int p_idx = trace[i];
516
50
    assert(p_idx >= 0);
517
50
    assert(curr_graph->sub_graphs);
518
50
    if (!curr_symbol_info->s_ref)
519
36
    {
520
36
      curr_symbol_info->s_ref = ccv_array_new(sizeof(int), curr_graph->sub_graphs->rnum, 0);
521
36
      curr_symbol_info->s_ref->rnum = curr_graph->sub_graphs->rnum;
522
36
      ccv_array_zero(curr_symbol_info->s_ref);
523
36
    } else 
if (14
curr_symbol_info->s_ref->rnum != curr_graph->sub_graphs->rnum14
)
524
8
      ccv_array_resize(curr_symbol_info->s_ref, curr_graph->sub_graphs->rnum);
525
50
    assert(p_idx >= 0 && p_idx < curr_symbol_info->s_ref->rnum);
526
50
    const int s_idx = *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx);
527
50
    ccv_nnc_symbolic_graph_t* const s = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(curr_graph->sub_graphs, p_idx);
528
50
    ccv_nnc_tensor_symbol_t new_symbol;
529
50
    ccv_nnc_tensor_symbol_info_t* new_symbol_info;
530
50
    // I need to find the symbol whether it exists or not before creating new one.
531
50
    if (!s_idx)
532
44
    {
533
44
      new_symbol = ccv_nnc_tensor_symbol_new(s, symbol_info->info, symbol_info->name);
534
44
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
535
44
      new_symbol_info->p_ref = curr_symbol.d + 1;
536
44
      *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx) = new_symbol.d + 1;
537
44
    } else {
538
6
      new_symbol.d = s_idx - 1;
539
6
      new_symbol.graph = s;
540
6
      assert(new_symbol.d >= 0 && new_symbol.d < s->tensor_symbol_info->rnum);
541
6
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
542
6
    }
543
50
    if (s->exec_idx)
544
50
    {
545
50
      assert(s->p); // This is a sub-graph.
546
50
      assert(s->exec_idx > 0 && s->exec_idx <= curr_graph->exec_symbol_info->rnum);
547
50
      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);
548
50
      switch (map_use)
549
50
      {
550
50
        case MAP_TENSOR_USE_AS_INPUT:
551
45
          _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, curr_symbol.d);
552
45
          break;
553
50
        case MAP_TENSOR_USE_AS_OUTPUT:
554
5
          _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, curr_symbol.d);
555
5
          break;
556
50
      }
557
50
    }
558
50
    // Move on.
559
50
    curr_symbol = new_symbol;
560
50
    curr_symbol_info = new_symbol_info;
561
50
    curr_graph = s;
562
50
  }
563
49
  return curr_symbol.d;
564
49
}
565
566
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)
567
68
{
568
68
  assert(graph && symbol.graph);
569
68
  assert(symbol.graph != graph);
570
68
  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);
571
68
  if (!symbol_info->alias_ref)
572
62
    return _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, symbol, map_use);
573
6
  const int d = symbol_info->alias_ref - 1;
574
6
  assert(d >= 0 && d < symbol.graph->tensor_symbol_info->rnum);
575
6
  const int map_d = _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, (ccv_nnc_tensor_symbol_t){
576
6
    .graph = symbol.graph,
577
6
    .d = d
578
6
  }, map_use);
579
6
  const ccv_nnc_tensor_symbol_t alias = ccv_nnc_tensor_symbol_alias_new(graph, (ccv_nnc_tensor_symbol_t){
580
6
    .graph = graph,
581
6
    .d = map_d
582
6
  }, symbol_info->ofs, symbol_info->inc, symbol_info->info, symbol_info->name);
583
6
  return alias.d;
584
6
}
585
586
int ccv_nnc_tensor_symbol_map_raw(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t symbol)
587
28.3k
{
588
28.3k
  if (symbol.d >= 0)
589
23.0k
    return symbol.graph != graph ? 
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, symbol, MAP_TENSOR_USE_AS_INPUT)61
:
symbol.d23.0k
;
590
5.29k
  if (symbol.graph == graph || 
symbol.d == CCV_NNC_NO_TENSOR_SYMBOL5.27k
)
591
5.29k
    return symbol.d;
592
1
  ccv_nnc_symbolic_graph_t* curr_graph = graph;
593
1
  int d;
594
2
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++1
)
595
1
    curr_graph = curr_graph->p;
596
1
  assert(curr_graph == symbol.graph);
597
1
  return CCV_NNC_ENCODE_WHILE_COUNT_SYMBOL(d);
598
1
}
599
600
void ccv_nnc_tensor_symbol_hookup(ccv_nnc_symbolic_graph_t* const src_graph, ccv_nnc_symbolic_graph_t* const dest_graph, const ccv_nnc_tensor_symbol_t src_tensor_symbol, const ccv_nnc_tensor_symbol_t dest_tensor_symbol)
601
35
{
602
35
  assert(src_graph != dest_graph);
603
35
  assert(src_graph->p == dest_graph || dest_graph->p == src_graph);
604
35
  assert(src_tensor_symbol.d >= 0);
605
35
  assert(dest_tensor_symbol.d >= 0);
606
35
  ccv_nnc_tensor_symbol_t tensor_symbol = src_tensor_symbol;
607
35
  if (tensor_symbol.graph != src_graph)
608
2
    tensor_symbol = (ccv_nnc_tensor_symbol_t){
609
2
      .graph = src_graph,
610
2
      .d = _ccv_nnc_symbolic_graph_map_tensor_symbol(src_graph, tensor_symbol, MAP_TENSOR_USE_AS_INPUT),
611
2
    };
612
35
  ccv_nnc_tensor_symbol_t sub_tensor_symbol = dest_tensor_symbol;
613
35
  if (sub_tensor_symbol.graph != dest_graph)
614
0
    sub_tensor_symbol = (ccv_nnc_tensor_symbol_t){
615
0
      .graph = dest_graph,
616
0
      .d = _ccv_nnc_symbolic_graph_map_tensor_symbol(dest_graph, sub_tensor_symbol, MAP_TENSOR_USE_AS_OUTPUT),
617
0
    };
618
35
  ccv_nnc_symbolic_graph_t* curr_graph = src_graph;
619
70
  while (curr_graph && 
curr_graph != dest_graph63
)
620
35
    curr_graph = curr_graph->p;
621
35
  ccv_nnc_symbolic_graph_t* graph;
622
35
  ccv_nnc_symbolic_graph_t* sub_graph;
623
35
  int map_use;
624
35
  if (curr_graph)
625
28
  {
626
28
    // src_graph is the sub graph, dest_graph is the parent graph.
627
28
    graph = dest_graph;
628
28
    sub_graph = src_graph;
629
28
    // Swap tensor_symbol and sub_tensor_symbol
630
28
    ccv_nnc_tensor_symbol_t x;
631
28
    CCV_SWAP(tensor_symbol, sub_tensor_symbol, x);
632
28
    map_use = MAP_TENSOR_USE_AS_OUTPUT;
633
28
  } else {
634
7
    graph = src_graph;
635
7
    sub_graph = dest_graph;
636
7
    map_use = MAP_TENSOR_USE_AS_INPUT;
637
7
  }
638
35
  ccv_nnc_symbolic_graph_t* p_graph = sub_graph;
639
35
  while (p_graph && p_graph->p != graph)
640
0
    p_graph = p_graph->p;
641
35
  assert(p_graph);
642
35
  if (p_graph != sub_graph)
643
0
  {
644
0
    sub_tensor_symbol.d = _ccv_nnc_symbolic_graph_map_tensor_symbol(p_graph, sub_tensor_symbol, map_use);
645
0
    sub_tensor_symbol.graph = p_graph;
646
0
    sub_graph = p_graph;
647
0
  }
648
35
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
649
35
  assert(sub_tensor_symbol.d < sub_graph->tensor_symbol_info->rnum);
650
35
  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);
651
35
  sub_tensor_info->p_ref = tensor_symbol.d + 1;
652
35
  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);
653
35
  if (!tensor_info->s_ref)
654
15
  {
655
15
    tensor_info->s_ref = ccv_array_new(sizeof(int), graph->sub_graphs->rnum, 0);
656
15
    tensor_info->s_ref->rnum = graph->sub_graphs->rnum;
657
15
    ccv_array_zero(tensor_info->s_ref);
658
20
  } else if (tensor_info->s_ref->rnum != graph->sub_graphs->rnum)
659
20
    ccv_array_resize(tensor_info->s_ref, graph->sub_graphs->rnum);
660
35
  const int p_idx = sub_graph->p_idx - 1;
661
35
  assert(p_idx >= 0 && p_idx < tensor_info->s_ref->rnum);
662
35
  const int s_idx = *(int*)ccv_array_get(tensor_info->s_ref, p_idx);
663
35
  assert(s_idx == 0); // Otherwise it is assigned before
664
35
  *(int*)ccv_array_get(tensor_info->s_ref, p_idx) = sub_tensor_symbol.d + 1;
665
35
  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, sub_graph->exec_idx - 1);
666
35
  switch (map_use)
667
35
  {
668
35
    case MAP_TENSOR_USE_AS_INPUT:
669
7
      _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, tensor_symbol.d);
670
7
      break;
671
35
    case MAP_TENSOR_USE_AS_OUTPUT:
672
28
      _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, tensor_symbol.d);
673
28
      break;
674
35
  }
675
35
}
676
677
void ccv_nnc_tensor_symbol_set_bypasses(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_map_t* const symbol_map, const int symbol_map_size)
678
11
{
679
11
  int i;
680
22
  for (i = 0; i < symbol_map_size; 
i++11
)
681
11
  {
682
11
    const ccv_nnc_tensor_symbol_t source = ccv_nnc_tensor_symbol_resolve(graph, symbol_map[i].source);
683
11
    const ccv_nnc_tensor_symbol_t destination = ccv_nnc_tensor_symbol_resolve(graph, symbol_map[i].destination);
684
11
    assert(source.graph == graph);
685
11
    assert(destination.graph == graph);
686
11
    assert(source.d < graph->tensor_symbol_info->rnum);
687
11
    assert(destination.d < graph->tensor_symbol_info->rnum);
688
11
    ccv_nnc_tensor_symbol_info_t* source_tensor_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, source.d);
689
11
    ccv_nnc_tensor_symbol_info_t* destination_tensor_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, destination.d);
690
11
    // Don't support parameterize with alias. The reason is that to support parameterized loop (for SSA), I choose
691
11
    // to simply reuse the piece of memory (allocating the same memory region to both, therefore to enable parameter
692
11
    // passing). For alias, it is not possible because alias can pointing to the tensors with different sizes, thus,
693
11
    // these pointed tensors cannot share the same memory region. The best way for alias to be parameterized is to
694
11
    // create a new tensor of the same size, transfer value over, and parameterized on that tensor instead.
695
11
    assert(!destination_tensor_symbol_info->alias_ref);
696
11
    assert(!source_tensor_symbol_info->alias_ref);
697
11
    destination_tensor_symbol_info->bypass_ref = source.d + 1;
698
11
    source_tensor_symbol_info->r_bypass_ref = destination.d + 1;
699
11
  }
700
11
}
701
702
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)
703
1
{
704
1
  assert(graph == tensor.graph);
705
1
  assert(tensor.d < graph->tensor_symbol_info->rnum);
706
1
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
707
1
  symbol_info->info = info;
708
1
  // It also need to propagate to assign_ref if needed.
709
1
  if (symbol_info->assign_ref)
710
0
  {
711
0
    ccv_nnc_tensor_symbol_info_t* const assign_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, symbol_info->assign_ref - 1);
712
0
    assign_info->info = info;
713
0
  }
714
1
  return 0;
715
1
}
716
717
ccv_nnc_tensor_param_t ccv_nnc_tensor_symbol_params(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor)
718
1.61k
{
719
1.61k
  assert(graph == tensor.graph);
720
1.61k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
721
1.61k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
722
1.61k
  return symbol_info->info;
723
1.61k
}
724
725
int ccv_nnc_tensor_symbol_set_flags(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const int flags)
726
9.24k
{
727
9.24k
  assert(graph == tensor.graph);
728
9.24k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
729
9.24k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
730
9.24k
  symbol_info->flags = flags;
731
9.24k
  // It also need to propagate to assign_ref if needed.
732
9.24k
  if (symbol_info->assign_ref)
733
1
  {
734
1
    ccv_nnc_tensor_symbol_info_t* const assign_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, symbol_info->assign_ref - 1);
735
1
    assign_info->flags = flags;
736
1
  }
737
9.24k
  return 0;
738
9.24k
}
739
740
int ccv_nnc_tensor_symbol_flags(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor)
741
11
{
742
11
  assert(graph == tensor.graph);
743
11
  assert(tensor.d < graph->tensor_symbol_info->rnum);
744
11
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
745
11
  return symbol_info->flags;
746
11
}
747
748
void ccv_nnc_tensor_symbol_free(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_tensor_symbol_t tensor)
749
16.1k
{
750
16.1k
  assert(graph == tensor.graph);
751
16.1k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
752
16.1k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
753
16.1k
  if (symbol_info->s_ref)
754
0
  {
755
0
    ccv_array_free(symbol_info->s_ref);
756
0
    symbol_info->s_ref = 0;
757
0
  }
758
16.1k
  if (symbol_info->name)
759
8
  {
760
8
    ccfree(symbol_info->name);
761
8
    symbol_info->name = 0;
762
8
  }
763
16.1k
  symbol_info->flags |= CCV_NNC_TENSOR_SYMBOL_DEAD;
764
16.1k
  int i;
765
32.1k
  for (i = graph->tensor_symbol_info->rnum - 1; i >= 0; 
i--16.0k
)
766
31.1k
    if (!CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
767
31.1k
    {
768
15.1k
      graph->tensor_symbol_info->rnum = i + 1;
769
15.1k
      break;
770
15.1k
    }
771
16.1k
  if (tensor.d < graph->tensor_symbol_info->rnum &&
772
16.1k
    
(15.0k
tensor.d < graph->reuse.tensor15.0k
||
graph->reuse.tensor < 015.0k
))
773
2.02k
    graph->reuse.tensor = tensor.d;
774
14.0k
  else if (graph->reuse.tensor >= graph->tensor_symbol_info->rnum)
775
1.00k
    graph->reuse.tensor = -1;
776
16.1k
}
777
778
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)
779
10.7k
{
780
10.7k
  exec_info->input_size = input_size;
781
10.7k
  exec_info->output_size = output_size;
782
10.7k
  if (input_size > 0 || 
output_size > 01.13k
)
783
10.7k
  {
784
10.7k
    if (!exec_info->inputs)
785
10.7k
      exec_info->inputs = ccmalloc(sizeof(int) * (input_size + output_size));
786
1
    else
787
1
      exec_info->inputs = ccrealloc(exec_info->inputs, sizeof(int) * (input_size + output_size));
788
10.7k
    exec_info->outputs = exec_info->inputs + input_size;
789
10.7k
  }
790
10.7k
  int i;
791
10.7k
  int tensor_memory = 0, tensor_formats = 0, tensor_datatypes = 0, tensor_auto = 0;
792
39.1k
  for (i = 0; i < input_size; 
i++28.3k
)
793
28.3k
  {
794
28.3k
    const int d = ccv_nnc_tensor_symbol_map_raw(graph, inputs[i]);
795
28.3k
    exec_info->inputs[i] = d;
796
28.3k
    if (d >= 0)
797
23.0k
    {
798
23.0k
      const ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
799
23.0k
      tensor_auto = tensor_auto || ccv_nnc_is_tensor_auto(tensor_info->info);
800
23.0k
      tensor_memory |= CCV_TENSOR_GET_MEMORY(tensor_info->info.type), tensor_formats |= tensor_info->info.format, tensor_datatypes |= tensor_info->info.datatype;
801
23.0k
    }
802
28.3k
  }
803
26.8k
  for (i = 0; i < output_size; 
i++16.0k
)
804
16.0k
  {
805
16.0k
    const int d = (outputs[i].graph != graph && 
outputs[i].d >= 01.20k
) ?
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, outputs[i], MAP_TENSOR_USE_AS_OUTPUT)5
:
outputs[i].d16.0k
;
806
16.0k
    exec_info->outputs[i] = d;
807
16.0k
    if (d >= 0)
808
14.8k
    {
809
14.8k
      const ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
810
14.8k
      tensor_auto = tensor_auto || 
ccv_nnc_is_tensor_auto(tensor_info->info)14.8k
;
811
14.8k
      tensor_memory |= CCV_TENSOR_GET_MEMORY(tensor_info->info.type), tensor_formats |= tensor_info->info.format, tensor_datatypes |= tensor_info->info.datatype;
812
14.8k
    }
813
16.0k
  }
814
10.7k
  // If there is no auto tensor, we try to find backend (we don't know which backend if the tensor is auto).
815
10.7k
  if (!tensor_auto)
816
10.7k
    exec_info->cmd.backend = ccv_nnc_cmd_find_backend(exec_info->cmd, tensor_memory, tensor_formats, tensor_datatypes);
817
10.7k
}
818
819
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)
820
10.7k
{
821
10.7k
  ccv_nnc_graph_exec_symbol_t symbol = {
822
10.7k
    .d = graph->exec_symbol_info->rnum,
823
10.7k
    .graph = graph
824
10.7k
  };
825
10.7k
  ccv_nnc_graph_exec_symbol_info_t symbol_info = {
826
10.7k
    .cmd = cmd,
827
10.7k
    .hint = ccv_nnc_no_hint,
828
10.7k
  };
829
10.7k
  if (name)
830
377
  {
831
377
    size_t n = strnlen(name, 63) + 1;
832
377
    symbol_info.name = (char*)ccmalloc(n);
833
377
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
834
377
    strncpy(symbol_info.name, name, n);
835
377
  }
836
10.7k
  _ccv_nnc_graph_exec_symbol_set_io(graph, &symbol_info, inputs, input_size, outputs, output_size);
837
10.7k
  if (graph->reuse.exec >= 0)
838
3.01k
  {
839
3.01k
    const int reuse_exec_d = graph->reuse.exec;
840
3.01k
    assert(reuse_exec_d < graph->exec_symbol_info->rnum);
841
3.01k
    *(ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, reuse_exec_d) = symbol_info;
842
3.01k
    int i;
843
3.01k
    graph->reuse.exec = -1;
844
5.01k
    for (i = reuse_exec_d + 1; i < graph->exec_symbol_info->rnum && 
graph->reuse.exec < 03.00k
;
i++2.00k
)
845
2.00k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i))->flags))
846
2.00k
        
graph->reuse.exec = i2.00k
;
847
3.01k
    symbol.d = reuse_exec_d;
848
3.01k
  } else
849
7.75k
    ccv_array_push(graph->exec_symbol_info, &symbol_info);
850
10.7k
  if (graph->hooks.graph_exec_symbol_new.func)
851
5.86k
    graph->hooks.graph_exec_symbol_new.func(graph->hooks.graph_exec_symbol_new.context, symbol, cmd, inputs, input_size, outputs, output_size, name);
852
10.7k
  return symbol;
853
10.7k
}
854
855
void* ccv_nnc_graph_exec_symbol_new_hook(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_graph_exec_symbol_new_hook_f hook, void* context)
856
2.02k
{
857
2.02k
  void* const prev = graph->hooks.graph_exec_symbol_new.context;
858
2.02k
  graph->hooks.graph_exec_symbol_new.func = hook;
859
2.02k
  graph->hooks.graph_exec_symbol_new.context = context;
860
2.02k
  return prev;
861
2.02k
}
862
863
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)
864
1
{
865
1
  assert(exec.graph == graph);
866
1
  assert(exec.d >= 0);
867
1
  assert(exec.d < graph->exec_symbol_info->rnum);
868
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);
869
1
  _ccv_nnc_graph_exec_symbol_set_io(graph, exec_info, inputs, input_size, outputs, output_size);
870
1
}
871
872
void ccv_nnc_graph_exec_symbol_set_peer(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec_symbol, const ccv_nnc_graph_exec_symbol_t peer_exec_symbol)
873
3.25k
{
874
3.25k
  assert(exec_symbol.graph == graph);
875
3.25k
  assert(exec_symbol.d >= 0);
876
3.25k
  assert(exec_symbol.d < graph->exec_symbol_info->rnum);
877
3.25k
  assert(peer_exec_symbol.graph == graph || peer_exec_symbol.graph == graph->peer);
878
3.25k
  assert(peer_exec_symbol.d >= 0);
879
3.25k
  if (peer_exec_symbol.graph == graph)
880
3.25k
    { assert(peer_exec_symbol.d < graph->exec_symbol_info->rnum); }
881
4
  else
882
4
    { assert(peer_exec_symbol.d < graph->peer->exec_symbol_info->rnum); }
883
3.25k
  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_symbol.d);
884
3.25k
  exec_info->peer_ref = peer_exec_symbol.d + 1;
885
3.25k
}
886
887
void ccv_nnc_graph_exec_symbol_set(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec, const ccv_nnc_cmd_t cmd)
888
713k
{
889
713k
  assert(graph == exec.graph);
890
713k
  assert(exec.d < graph->exec_symbol_info->rnum);
891
713k
  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);
892
713k
  symbol_info->cmd = cmd;
893
713k
}
894
895
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)
896
2
{
897
2
  assert(graph == exec.graph);
898
2
  assert(exec.d < graph->exec_symbol_info->rnum);
899
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);
900
2
  return symbol_info->cmd;
901
2
}
902
903
void 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)
904
3.93k
{
905
3.93k
  assert(graph == exec.graph);
906
3.93k
  assert(exec.d < graph->exec_symbol_info->rnum);
907
3.93k
  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);
908
3.93k
  symbol_info->hint = hint;
909
3.93k
}
910
911
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)
912
14.3k
{
913
14.3k
  assert(graph == source.graph);
914
14.3k
  assert(graph == destination.graph);
915
14.3k
  assert(source.d < graph->exec_symbol_info->rnum);
916
14.3k
  assert(destination.d < graph->exec_symbol_info->rnum);
917
14.3k
  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);
918
14.3k
  if (!src_symbol_info->outgoings)
919
9.28k
    src_symbol_info->outgoings = ccv_array_new(sizeof(int32_t), 1, 0);
920
5.03k
  else {
921
5.03k
    int i;
922
5.03k
    // Check if this is already connected, if so, skip.
923
9.52k
    for (i = 0; i < src_symbol_info->outgoings->rnum; 
i++4.48k
)
924
6.15k
      if (*(int*)ccv_array_get(src_symbol_info->outgoings, i) == destination.d)
925
1.66k
        return -1;
926
5.03k
  }
927
14.3k
  ccv_array_push(src_symbol_info->outgoings, &destination.d);
928
12.6k
  return 0;
929
14.3k
}
930
931
void ccv_nnc_graph_exec_symbol_io(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t symbol, const int** const inputs, int* const input_size, const int** const outputs, int* const output_size)
932
10.0k
{
933
10.0k
  assert(graph == symbol.graph);
934
10.0k
  assert(symbol.d < graph->exec_symbol_info->rnum);
935
10.0k
  const ccv_nnc_graph_exec_symbol_info_t* const symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, symbol.d);
936
10.0k
  if (inputs)
937
7.03k
    *inputs = symbol_info->inputs;
938
10.0k
  if (input_size)
939
7.03k
    *input_size = symbol_info->input_size;
940
10.0k
  if (outputs)
941
3.02k
    *outputs = symbol_info->outputs;
942
10.0k
  if (output_size)
943
3.02k
    *output_size = symbol_info->output_size;
944
10.0k
}
945
946
void ccv_nnc_graph_exec_symbol_to(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t symbol, const int** const tos, int* const to_size)
947
7.06k
{
948
7.06k
  assert(graph == symbol.graph);
949
7.06k
  assert(symbol.d < graph->exec_symbol_info->rnum);
950
7.06k
  assert(tos);
951
7.06k
  assert(to_size);
952
7.06k
  const ccv_nnc_graph_exec_symbol_info_t* const symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, symbol.d);
953
7.06k
  if (!symbol_info->outgoings)
954
1.01k
  {
955
1.01k
    *tos = 0;
956
1.01k
    *to_size = 0;
957
1.01k
    return;
958
1.01k
  }
959
6.05k
  *to_size = symbol_info->outgoings->rnum;
960
6.05k
  *tos = (int*)ccv_array_get(symbol_info->outgoings, 0);
961
6.05k
}
962
963
int ccv_nnc_graph_exec_symbol_count(const ccv_nnc_symbolic_graph_t* const graph)
964
1.00k
{
965
1.00k
  return graph->exec_symbol_info->rnum;
966
1.00k
}
967
968
int ccv_nnc_tensor_symbol_count(const ccv_nnc_symbolic_graph_t* const graph)
969
3.43k
{
970
3.43k
  return graph->tensor_symbol_info->rnum;
971
3.43k
}
972
973
struct ccv_nnc_symbolic_graph_iter_s {
974
  int idx;
975
  const ccv_nnc_symbolic_graph_t* graph;
976
  ccv_nnc_graph_visit_t* visit;
977
  ccv_nnc_tensor_symbol_t* symbol_cache;
978
  int symbol_cache_size;
979
};
980
981
ccv_nnc_symbolic_graph_iter_t* ccv_nnc_symbolic_graph_iter_new(const ccv_nnc_symbolic_graph_t* const graph, 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)
982
3.43k
{
983
3.43k
  const ccv_nnc_graph_exec_symbol_t* const graph_sources = sources ? 
sources0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->sources, 0);
984
3.43k
  const int graph_source_size = source_size ? 
source_size0
: graph->sources->rnum;
985
3.43k
  const ccv_nnc_graph_exec_symbol_t* const graph_destinations = destinations ? 
destinations0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->destinations, 0);
986
3.43k
  const int graph_destination_size = destination_size ? 
destination_size0
: graph->destinations->rnum;
987
3.43k
  ccv_nnc_symbolic_graph_iter_t* const iter = (ccv_nnc_symbolic_graph_iter_t*)cccalloc(1, sizeof(ccv_nnc_symbolic_graph_iter_t));
988
3.43k
  iter->idx = -1;
989
3.43k
  iter->graph = graph;
990
3.43k
  iter->visit = ccv_nnc_graph_visit_new(graph, (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, 0), graph->exec_symbol_info->rnum, graph_sources, graph_source_size, graph_destinations, graph_destination_size, 0);
991
3.43k
  return iter;
992
3.43k
}
993
994
int ccv_nnc_symbolic_graph_iter_next(ccv_nnc_symbolic_graph_iter_t* const iter)
995
1.35M
{
996
1.35M
  ++iter->idx;
997
1.35M
  return (iter->idx < iter->visit->size);
998
1.35M
}
999
1000
void ccv_nnc_graph_exec_symbol_from_iter(ccv_nnc_symbolic_graph_iter_t* const iter, ccv_nnc_cmd_t* const cmd, ccv_nnc_hint_t* const hint, int* const flags, char** const name)
1001
233k
{
1002
233k
  if (iter->idx >= iter->visit->size)
1003
0
    return;
1004
233k
  const ccv_nnc_graph_exec_symbol_info_t* const exec_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(iter->graph->exec_symbol_info, iter->visit->node[iter->idx].index);
1005
233k
  if (cmd)
1006
233k
    *cmd = exec_info->cmd;
1007
233k
  if (hint)
1008
0
    *hint = exec_info->hint;
1009
233k
  if (flags)
1010
0
    *flags = exec_info->flags;
1011
233k
  if (name)
1012
0
    *name = exec_info->name;
1013
233k
}
1014
1015
void ccv_nnc_tensor_symbol_io_from_iter(ccv_nnc_symbolic_graph_iter_t* const iter, ccv_nnc_tensor_symbol_t** const inputs, int* const input_size,  ccv_nnc_tensor_symbol_t** const outputs, int* const output_size)
1016
1.35M
{
1017
1.35M
  if (iter->idx >= iter->visit->size)
1018
0
    return;
1019
1.35M
  const ccv_nnc_graph_exec_symbol_info_t* const exec_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(iter->graph->exec_symbol_info, iter->visit->node[iter->idx].index);
1020
1.35M
  if (exec_info->input_size + exec_info->output_size > iter->symbol_cache_size)
1021
10.2k
  {
1022
10.2k
    iter->symbol_cache_size = exec_info->input_size + exec_info->output_size;
1023
10.2k
    if (!iter->symbol_cache)
1024
3.43k
      iter->symbol_cache = ccmalloc(sizeof(ccv_nnc_tensor_symbol_t) * iter->symbol_cache_size);
1025
6.85k
    else
1026
6.85k
      iter->symbol_cache = ccrealloc(iter->symbol_cache, sizeof(ccv_nnc_tensor_symbol_t) * iter->symbol_cache_size);
1027
10.2k
  }
1028
1.35M
  int i;
1029
1.35M
  if (inputs)
1030
1.35M
  {
1031
1.35M
    *inputs = iter->symbol_cache;
1032
6.72M
    for (i = 0; i < exec_info->input_size; 
i++5.37M
)
1033
5.37M
      iter->symbol_cache[i] = (ccv_nnc_tensor_symbol_t){
1034
5.37M
        .graph = (exec_info->inputs[i] == CCV_NNC_NO_TENSOR_SYMBOL ? 
01.48M
:
iter->graph3.89M
),
1035
5.37M
        .d = exec_info->inputs[i],
1036
5.37M
      };
1037
1.35M
  }
1038
1.35M
  if (input_size)
1039
1.35M
    *input_size = exec_info->input_size;
1040
1.35M
  if (outputs)
1041
0
  {
1042
0
    *outputs = iter->symbol_cache + exec_info->input_size;
1043
0
    for (i = 0; i < exec_info->output_size; i++)
1044
0
      iter->symbol_cache[i + exec_info->input_size] = (ccv_nnc_tensor_symbol_t){
1045
0
        .graph = (exec_info->outputs[i] == CCV_NNC_NO_TENSOR_SYMBOL ? 0 : iter->graph),
1046
0
        .d = exec_info->outputs[i],
1047
0
      };
1048
0
  }
1049
1.35M
  if (output_size)
1050
0
    *output_size = exec_info->output_size;
1051
1.35M
}
1052
1053
void ccv_nnc_symbolic_graph_iter_free(ccv_nnc_symbolic_graph_iter_t* const iter)
1054
3.43k
{
1055
3.43k
  ccv_nnc_graph_visit_free(iter->visit);
1056
3.43k
  if (iter->symbol_cache)
1057
3.43k
    ccfree(iter->symbol_cache);
1058
3.43k
  ccfree(iter);
1059
3.43k
}
1060
1061
static inline void _ccv_nnc_graph_exec_symbol_free(ccv_nnc_graph_exec_symbol_info_t* const symbol_info, const int zeroing)
1062
10.8k
{
1063
10.8k
  if (symbol_info->name)
1064
10.8k
    
ccfree405
(symbol_info->name)405
;
1065
10.8k
  if (symbol_info->_heap_graph_ref)
1066
10.8k
    
ccfree7
(symbol_info->_heap_graph_ref)7
;
1067
10.8k
  ccv_array_t* outgoings = symbol_info->outgoings;
1068
10.8k
  if (outgoings)
1069
9.31k
    ccv_array_free(outgoings);
1070
10.8k
  // We allocate inputs & outputs in continuous fashion, therefore, only need to free the input array.
1071
10.8k
  if (symbol_info->inputs)
1072
10.8k
    
ccfree10.7k
(symbol_info->inputs)10.7k
;
1073
10.8k
  if (symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE)
1074
24
    if (symbol_info->p_while.inputs)
1075
24
      
ccfree19
(symbol_info->p_while.inputs)19
;
1076
10.8k
  if (zeroing)
1077
9.08k
  {
1078
9.08k
    symbol_info->name = 0;
1079
9.08k
    symbol_info->_heap_graph_ref = 0;
1080
9.08k
    symbol_info->outgoings = 0;
1081
9.08k
    symbol_info->inputs = 0;
1082
9.08k
    symbol_info->input_size = 0;
1083
9.08k
    symbol_info->outputs = 0;
1084
9.08k
    symbol_info->output_size = 0;
1085
9.08k
  }
1086
10.8k
}
1087
1088
void ccv_nnc_graph_exec_symbol_free(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t symbol)
1089
9.08k
{
1090
9.08k
  assert(graph == symbol.graph);
1091
9.08k
  assert(symbol.d < graph->exec_symbol_info->rnum);
1092
9.08k
  // If any of the exec symbol have reference to it, has to remove that.
1093
9.08k
  int i, j, k;
1094
9.08k
  ccv_nnc_graph_exec_symbol_info_t* const free_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, symbol.d);
1095
67.7k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++58.7k
)
1096
58.7k
    if (i != symbol.d)
1097
49.6k
    {
1098
49.6k
      ccv_nnc_graph_exec_symbol_info_t* const symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1099
49.6k
      if (symbol_info->outgoings)
1100
62.9k
        
for (j = 0; 31.4k
j < symbol_info->outgoings->rnum;
j++31.5k
)
1101
41.6k
          if (*(int*)ccv_array_get(symbol_info->outgoings, j) == symbol.d)
1102
10.1k
          {
1103
10.1k
            if (j < symbol_info->outgoings->rnum - 1)
1104
37
              *(int*)ccv_array_get(symbol_info->outgoings, j) = *(int*)ccv_array_get(symbol_info->outgoings, symbol_info->outgoings->rnum - 1);
1105
10.1k
            --symbol_info->outgoings->rnum;
1106
10.1k
            if (free_symbol_info->outgoings)
1107
14.1k
              
for (k = 0; 7.06k
k < free_symbol_info->outgoings->rnum;
k++7.07k
)
1108
7.07k
                ccv_array_add_unique_int(symbol_info->outgoings, *(int*)ccv_array_get(free_symbol_info->outgoings, k));
1109
10.1k
            break;
1110
10.1k
          }
1111
49.6k
    }
1112
9.08k
  // Deallocate any memory for exec symbol.
1113
9.08k
  _ccv_nnc_graph_exec_symbol_free(free_symbol_info, 1);
1114
9.08k
  free_symbol_info->flags = CCV_NNC_GRAPH_EXEC_DEAD; // Mark this as dead.
1115
9.08k
  // If everything from symbol.d to the end of the graph is dead, we can reclaim this memory.
1116
18.1k
  for (i = graph->exec_symbol_info->rnum - 1; i >= 0; 
i--9.06k
)
1117
17.1k
    if (!CCV_NNC_GRAPH_EXEC_IS_DEAD(((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i))->flags))
1118
17.1k
    {
1119
8.07k
      graph->exec_symbol_info->rnum = i + 1;
1120
8.07k
      break;
1121
8.07k
    }
1122
9.08k
  // Loop over sources and destinations to remove this.
1123
9.08k
  if (graph->sources)
1124
68
    
for (i = 0; 27
i < graph->sources->rnum;
i++41
)
1125
49
      if (*(int*)ccv_array_get(graph->sources, i) == symbol.d)
1126
8
      {
1127
8
        if (i < graph->sources->rnum - 1)
1128
1
          *(int*)ccv_array_get(graph->sources, i) = *(int*)ccv_array_get(graph->sources, graph->sources->rnum - 1);
1129
8
        --graph->sources->rnum;
1130
8
        break;
1131
8
      }
1132
9.08k
  if (graph->destinations)
1133
57
    
for (i = 0; 27
i < graph->destinations->rnum;
i++30
)
1134
38
      if (*(int*)ccv_array_get(graph->destinations, i) == symbol.d)
1135
8
      {
1136
8
        if (i < graph->destinations->rnum - 1)
1137
3
          *(int*)ccv_array_get(graph->destinations, i) = *(int*)ccv_array_get(graph->destinations, graph->destinations->rnum - 1);
1138
8
        --graph->destinations->rnum;
1139
8
        break;
1140
8
      }
1141
9.08k
  if (symbol.d < graph->exec_symbol_info->rnum &&
1142
9.08k
    
(7.06k
symbol.d < graph->reuse.exec7.06k
||
graph->reuse.exec < 07.06k
))
1143
2.02k
    graph->reuse.exec = symbol.d;
1144
7.05k
  else if (graph->reuse.exec >= graph->exec_symbol_info->rnum)
1145
1.00k
    graph->reuse.exec = -1;
1146
9.08k
}
1147
1148
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)
1149
1
{
1150
1
  assert(graph == source.graph);
1151
1
  assert(graph == destination.graph);
1152
1
  assert(source.d < graph->exec_symbol_info->rnum);
1153
1
  assert(destination.d < graph->exec_symbol_info->rnum);
1154
1
  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);
1155
1
  if (!src_symbol_info->outgoings)
1156
0
    return -1;
1157
1
  ccv_nnc_graph_exec_symbol_info_t* const dest_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, destination.d);
1158
1
  int i, j;
1159
1
  // Check if this is already disjoined, if so, skip.
1160
1
  for (i = 0; i < src_symbol_info->outgoings->rnum; 
i++0
)
1161
1
    if (*(int*)ccv_array_get(src_symbol_info->outgoings, i) == destination.d)
1162
1
    {
1163
1
      if (i < src_symbol_info->outgoings->rnum - 1)
1164
1
        *(int*)ccv_array_get(src_symbol_info->outgoings, i) = *(int*)ccv_array_get(src_symbol_info->outgoings, src_symbol_info->outgoings->rnum - 1);
1165
1
      --src_symbol_info->outgoings->rnum;
1166
1
      if (dest_symbol_info->outgoings)
1167
0
        for (j = 0; j < dest_symbol_info->outgoings->rnum; j++)
1168
0
          ccv_array_add_unique_int(src_symbol_info->outgoings, *(int*)ccv_array_get(dest_symbol_info->outgoings, j));
1169
1
      return 0;
1170
1
    }
1171
1
  
return -10
;
1172
1
}
1173
1174
51.7k
#define CCV_NNC_IS_AUTOGEN_ALL_EXECS(x) ((x) & CCV_NNC_AUTOGEN_ALL_EXECS)
1175
247
#define CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(x) ((x) & CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS)
1176
1177
int ccv_nnc_over_tensor_symbol_aliases(const ccv_nnc_tensor_symbol_info_t* const tensor_a, const ccv_nnc_tensor_symbol_info_t* const tensor_b)
1178
71
{
1179
71
  int i;
1180
71
  const int* inc = tensor_a->inc;
1181
71
  // Only can compare if the inc is the same, otherwise, we can only assume it overlaps.
1182
71
  if (memcmp(inc, tensor_b->inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC) != 0)
1183
0
    return 1;
1184
71
  const int* ofs = tensor_a->ofs;
1185
71
  const int* dim = tensor_a->info.dim;
1186
165
  for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && dim[i] && 
tensor_b->info.dim[i]126
;
i++94
)
1187
126
    if (ccv_min(ofs[i] + dim[i], tensor_b->ofs[i] + tensor_b->info.dim[i]) <= ccv_max(ofs[i], tensor_b->ofs[i]))
1188
126
      
return 032
; // Cannot overlap.
1189
71
  
return 139
;
1190
71
}
1191
1192
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)
1193
247
{
1194
247
  int i, j, x, y;
1195
290
  for (i = 0; i < exec_size; 
i++43
)
1196
43
    if (execs[i].graph == graph)
1197
43
    {
1198
43
      assert(execs[i].d >= 0);
1199
43
      assert(execs[i].d < graph->exec_symbol_info->rnum);
1200
43
    }
1201
247
  if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
exec_size51
)
1202
11
    { assert(execs); }
1203
247
  const int exec_total_size = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
graph->exec_symbol_info->rnum196
:
exec_size51
;
1204
1.39k
  for (i = 0; i < exec_total_size; 
i++1.14k
)
1205
1.14k
  {
1206
1.14k
    if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[i].graph != graph43
)
1207
0
      continue;
1208
1.14k
    int idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
i1.10k
:
execs[i].d43
;
1209
1.14k
    ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, idx);
1210
1.14k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
1211
1.14k
      
continue0
;
1212
1.14k
    // Autogen for sub-graphs.
1213
1.14k
    if (CCV_NNC_GRAPH_REF(symbol_info)[0])
1214
26
      ccv_nnc_graph_exec_symbol_autogen(*(ccv_nnc_symbolic_graph_t**)ccv_array_get(graph->sub_graphs, CCV_NNC_GRAPH_REF(symbol_info)[0] - 1), execs, exec_size, flags);
1215
1.14k
  }
1216
1.39k
  for (i = 0; i < exec_total_size; 
i++1.14k
)
1217
1.14k
  {
1218
1.14k
    if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[i].graph != graph43
)
1219
0
      continue;
1220
1.14k
    int a_idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
i1.10k
:
execs[i].d43
;
1221
1.14k
    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);
1222
1.14k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(a_symbol_info->flags))
1223
1.14k
      
continue0
;
1224
15.8k
    
for (j = i + 1; 1.14k
j < exec_total_size;
j++14.7k
)
1225
14.7k
    {
1226
14.7k
      if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[j].graph != graph69
)
1227
0
        continue;
1228
14.7k
      int b_idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
j14.6k
:
execs[j].d69
;
1229
14.7k
      // Skip if they are the same.
1230
14.7k
      if (a_idx == b_idx)
1231
0
        continue;
1232
14.7k
      ccv_nnc_graph_exec_symbol_info_t* b_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, b_idx);
1233
14.7k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(b_symbol_info->flags))
1234
14.7k
        
continue0
;
1235
14.7k
      int b_to_a = 0;
1236
61.5k
      for (x = 0; x < a_symbol_info->input_size && 
!b_to_a46.9k
;
x++46.8k
)
1237
46.8k
      {
1238
46.8k
        int a = a_symbol_info->inputs[x];
1239
46.8k
        if (a < 0)
1240
11.0k
          continue;
1241
35.8k
        // Handle alias as well.
1242
35.8k
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
1243
35.8k
        if (a_tensor_info->alias_ref)
1244
396
          a = a_tensor_info->alias_ref - 1;
1245
104k
        for (y = 0; y < b_symbol_info->output_size && 
!b_to_a68.8k
;
y++68.8k
)
1246
68.8k
        {
1247
68.8k
          int b = b_symbol_info->outputs[y];
1248
68.8k
          if (b < 0)
1249
4.54k
            continue;
1250
64.2k
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
1251
64.2k
          if (b_tensor_info->alias_ref)
1252
569
            b = b_tensor_info->alias_ref - 1;
1253
64.2k
          if (a == b && // This two have matching inputs and outputs.
1254
64.2k
            
(160
!a_tensor_info->alias_ref160
||
1255
160
             
!b_tensor_info->alias_ref6
|| // If any of them are not alias, the must overlap, you can concatenate.
1256
160
             
ccv_nnc_over_tensor_symbol_aliases(a_tensor_info, b_tensor_info)6
)) // Otherwise, we explicitly check whether it overlaps, if it does, concatenate.
1257
154
            b_to_a = 1;
1258
64.2k
        }
1259
35.8k
      }
1260
14.7k
      if (b_to_a)
1261
154
      {
1262
154
        if (execs)
1263
0
          ccv_nnc_graph_exec_symbol_concat(graph, execs[j], execs[i]);
1264
154
        else
1265
154
          ccv_nnc_graph_exec_symbol_concat(graph,
1266
154
            (ccv_nnc_graph_exec_symbol_t) {
1267
154
              .d = j,
1268
154
              .graph = graph
1269
154
            }, (ccv_nnc_graph_exec_symbol_t) {
1270
154
              .d = i,
1271
154
              .graph = graph
1272
154
            }
1273
154
          );
1274
154
      }
1275
14.7k
      int a_to_b = 0;
1276
42.2k
      for (x = 0; x < a_symbol_info->output_size && 
!a_to_b27.7k
;
x++27.5k
)
1277
27.5k
      {
1278
27.5k
        int a = a_symbol_info->outputs[x];
1279
27.5k
        if (a < 0)
1280
1.73k
          continue;
1281
25.8k
        // Handle alias as well.
1282
25.8k
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
1283
25.8k
        if (a_tensor_info->alias_ref)
1284
151
          a = a_tensor_info->alias_ref - 1;
1285
116k
        for (y = 0; y < b_symbol_info->input_size && 
!a_to_b90.8k
;
y++90.2k
)
1286
90.2k
        {
1287
90.2k
          int b = b_symbol_info->inputs[y];
1288
90.2k
          if (b < 0)
1289
22.2k
            continue;
1290
68.0k
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
1291
68.0k
          if (b_tensor_info->alias_ref)
1292
861
            b = b_tensor_info->alias_ref - 1;
1293
68.0k
          if (a == b && // This two have matching inputs and outputs.
1294
68.0k
            
(945
!a_tensor_info->alias_ref945
||
1295
945
             
!b_tensor_info->alias_ref25
|| // If any of them are not alias, the must overlap, you can concatenate.
1296
945
             
ccv_nnc_over_tensor_symbol_aliases(a_tensor_info, b_tensor_info)19
)) // Otherwise, we explicitly check whether it overlaps, if it does, concatenate.
1297
940
            a_to_b = 1;
1298
68.0k
        }
1299
25.8k
      }
1300
14.7k
      if (a_to_b)
1301
940
      {
1302
940
        if (execs)
1303
41
          ccv_nnc_graph_exec_symbol_concat(graph, execs[i], execs[j]);
1304
899
        else
1305
899
          ccv_nnc_graph_exec_symbol_concat(graph,
1306
899
            (ccv_nnc_graph_exec_symbol_t) {
1307
899
              .d = i,
1308
899
              .graph = graph
1309
899
            }, (ccv_nnc_graph_exec_symbol_t) {
1310
899
              .d = j,
1311
899
              .graph = graph
1312
899
            }
1313
899
          );
1314
940
      }
1315
14.7k
    }
1316
1.14k
  }
1317
247
  // If flag says so, loop over to find sources / destinations too.
1318
247
  if (CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(flags))
1319
247
  {
1320
226
    uint8_t* flags = (uint8_t*)cccalloc(sizeof(uint8_t), graph->exec_symbol_info->rnum);
1321
2.21k
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++1.98k
)
1322
1.98k
    {
1323
1.98k
      ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1324
1.98k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
1325
1.98k
      {
1326
6
        flags[i] = 3; // Skip.
1327
6
        continue;
1328
6
      }
1329
1.98k
      if (symbol_info->outgoings && 
symbol_info->outgoings->rnum1.50k
)
1330
1.50k
      {
1331
1.50k
        flags[i] |= 2;
1332
3.96k
        for (j = 0; j < symbol_info->outgoings->rnum; 
j++2.45k
)
1333
2.45k
          flags[*(int*)ccv_array_get(symbol_info->outgoings, j)] |= 1;
1334
1.50k
      }
1335
1.98k
    }
1336
226
    if (!graph->sources)
1337
140
      graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1338
86
    else
1339
86
      ccv_array_clear(graph->sources);
1340
226
    if (!graph->destinations)
1341
140
      graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1342
86
    else
1343
86
      ccv_array_clear(graph->destinations);
1344
2.21k
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++1.98k
)
1345
1.98k
    {
1346
1.98k
      if (flags[i] == 3)
1347
1.24k
        continue;
1348
745
      ccv_nnc_graph_exec_symbol_t exec = {
1349
745
        .d = i,
1350
745
        .graph = graph,
1351
745
      };
1352
745
      if (!(flags[i] & 1))
1353
355
        ccv_array_push(graph->sources, &exec);
1354
745
      if (!(flags[i] & 2))
1355
482
        ccv_array_push(graph->destinations, &exec);
1356
745
    }
1357
226
    ccfree(flags);
1358
226
  }
1359
247
  return 0;
1360
247
}
1361
1362
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_sources(const ccv_nnc_symbolic_graph_t* const graph)
1363
233
{
1364
233
  return graph->sources ? (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->sources, 0) : 
00
;
1365
233
}
1366
1367
void ccv_nnc_symbolic_graph_add_source(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t source)
1368
17
{
1369
17
  if (!graph->sources)
1370
0
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1371
17
  assert(source.graph == graph);
1372
17
  ccv_array_push(graph->sources, &source);
1373
17
}
1374
1375
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)
1376
15
{
1377
15
  if (!graph->sources)
1378
11
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1379
4
  else
1380
4
    ccv_array_clear(graph->sources);
1381
15
  int i;
1382
30
  for (i = 0; i < source_size; 
i++15
)
1383
15
    ccv_nnc_symbolic_graph_add_source(graph, sources[i]);
1384
15
}
1385
1386
int ccv_nnc_symbolic_graph_source_size(const ccv_nnc_symbolic_graph_t* const graph)
1387
234
{
1388
234
  return graph->sources ? graph->sources->rnum : 
00
;
1389
234
}
1390
1391
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_destinations(const ccv_nnc_symbolic_graph_t* const graph)
1392
251
{
1393
251
  return graph->destinations ? (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->destinations, 0) : 
00
;
1394
251
}
1395
1396
void ccv_nnc_symbolic_graph_add_destination(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t destination)
1397
119
{
1398
119
  if (!graph->destinations)
1399
0
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1400
119
  assert(destination.graph == graph);
1401
119
  ccv_array_push(graph->destinations, &destination);
1402
119
}
1403
1404
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)
1405
23
{
1406
23
  if (!graph->destinations)
1407
11
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1408
12
  else
1409
12
    ccv_array_clear(graph->destinations);
1410
23
  int i;
1411
126
  for (i = 0; i < destination_size; 
i++103
)
1412
103
    ccv_nnc_symbolic_graph_add_destination(graph, destinations[i]);
1413
23
}
1414
1415
int ccv_nnc_symbolic_graph_destination_size(const ccv_nnc_symbolic_graph_t* const graph)
1416
252
{
1417
252
  return graph->destinations ? graph->destinations->rnum : 
00
;
1418
252
}
1419
1420
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)
1421
625
{
1422
625
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1423
596
    fputc('{', out);
1424
625
  if (symbol_info->name)
1425
282
    fputs(symbol_info->name, out);
1426
343
  else
1427
343
    fprintf(out, "node%d", index);
1428
625
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1429
596
  {
1430
596
    fputs("|Command: ", out);
1431
596
    fputs(ccv_nnc_cmd_name(symbol_info->cmd.cmd), out);
1432
596
    fputc('}', out);
1433
596
  }
1434
625
}
1435
1436
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)
1437
2.01k
{
1438
2.01k
  // if it has an alias pointer, or, it is a long form.
1439
2.01k
  if ((flags == CCV_NNC_LONG_DOT_GRAPH || 
alias_info79
) &&
!html_like1.96k
)
1440
1.87k
    fputc('{', out);
1441
2.01k
  if (symbol_info->name)
1442
914
    fputs(symbol_info->name, out);
1443
1.09k
  else
1444
1.09k
    fprintf(out, "tensor%d", index);
1445
2.01k
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1446
1.93k
  {
1447
1.93k
    int flag = -1;
1448
1.93k
    if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS)
1449
21
      flag = fputs(" (0", out); // Output if it is zero init'ed.
1450
1.91k
    else if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ONES)
1451
1
        flag = fputs(" (1", out); // Output if it is one init'ed.
1452
1.93k
    if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_TAPE_VAR)
1453
16
      flag = (flag >= 0) ? 
fputs(",t", out)0
: fputs(" (t", out); // Output is a tape variable
1454
1.93k
    if (CCV_TENSOR_GET_MEMORY(symbol_info->info.type) == CCV_TENSOR_GPU_MEMORY &&
1455
1.93k
      
CCV_TENSOR_GET_DEVICE694
(symbol_info->info.type) != CCV_COMPUTE_DEVICE_ANY694
)
1456
694
      flag = (flag >= 0) ? 
fprintf(out, ",d%d", 12
CCV_TENSOR_GET_DEVICE_ID12
(symbol_info->info.type)) :
fprintf(out, " (d%d", 682
CCV_TENSOR_GET_DEVICE_ID682
(symbol_info->info.type));
1457
1.93k
    if (flag >= 0)
1458
720
      fputs(")", out);
1459
1.93k
  }
1460
2.01k
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1461
1.93k
  {
1462
1.93k
    int i;
1463
1.93k
    if (html_like)
1464
86
      fprintf(out, "</td><td>%d", symbol_info->info.dim[0]);
1465
1.84k
    else
1466
1.84k
      fprintf(out, "|%d", symbol_info->info.dim[0]);
1467
4.05k
    for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && symbol_info->info.dim[i]; 
i++2.12k
)
1468
2.12k
      fprintf(out, "x%d", symbol_info->info.dim[i]);
1469
1.93k
  }
1470
2.01k
  if (alias_info)
1471
165
  {
1472
165
    if (html_like)
1473
0
      fputs("</td><td border=\"0\">as. ", out);
1474
165
    else
1475
165
      fputs("|as. ", out);
1476
165
    if (alias_info->name)
1477
97
      fputs(alias_info->name, out);
1478
68
    else
1479
68
      fprintf(out, "tensor%d", symbol_info->alias_ref - 1);
1480
165
    if (flags == CCV_NNC_LONG_DOT_GRAPH)
1481
133
    {
1482
133
      int flag = -1;
1483
133
      if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS)
1484
12
        flag = fputs(" (0", out); // Output if it is zero init'ed.
1485
121
      else if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ONES)
1486
0
        flag = fputs(" (1", out); // Output if it is one init'ed.
1487
133
      if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_TAPE_VAR)
1488
0
        flag = (flag >= 0) ? fputs(",t", out) : fputs(" (t", out); // Output is a tape variable
1489
133
      if (CCV_TENSOR_GET_MEMORY(alias_info->info.type) == CCV_TENSOR_GPU_MEMORY &&
1490
133
        
CCV_TENSOR_GET_DEVICE8
(alias_info->info.type) != CCV_COMPUTE_DEVICE_ANY8
)
1491
8
        flag = (flag >= 0) ? 
fprintf(out, ",d%d", 4
CCV_TENSOR_GET_DEVICE_ID4
(alias_info->info.type)) :
fprintf(out, " (d%d", 4
CCV_TENSOR_GET_DEVICE_ID4
(alias_info->info.type));
1492
133
      if (flag >= 0)
1493
16
        fputs(")", out);
1494
133
    }
1495
165
  }
1496
2.01k
  if ((flags == CCV_NNC_LONG_DOT_GRAPH || 
alias_info79
) &&
!html_like1.96k
)
1497
1.87k
    fputc('}', out);
1498
2.01k
}
1499
1500
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)
1501
625
{
1502
625
  fprintf(out, "node%d [shape=record,label=\"", index);
1503
625
  _ccv_nnc_symbolic_graph_dot_exec_symbol(index, exec_symbol_info, flags, out);
1504
625
  int i;
1505
625
  if (exec_symbol_info->input_size > 0)
1506
560
  {
1507
560
    fputs("|{Input", out);
1508
1.97k
    for (i = 0; i < exec_symbol_info->input_size; 
i++1.41k
)
1509
1.41k
    {
1510
1.41k
      if (exec_symbol_info->inputs[i] >= 0)
1511
1.15k
      {
1512
1.15k
        fputc('|', out);
1513
1.15k
        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]);
1514
1.15k
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)103
ccv_array_get103
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
01.05k
;
1515
1.15k
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 0, flags, out);
1516
1.15k
      } else
1517
251
        fputs("|-", out);
1518
1.41k
    }
1519
560
    fputc('}', out);
1520
560
  }
1521
625
  if (exec_symbol_info->output_size > 0)
1522
605
  {
1523
605
    fputs("|{Output", out);
1524
1.40k
    for (i = 0; i < exec_symbol_info->output_size; 
i++801
)
1525
801
    {
1526
801
      if (exec_symbol_info->outputs[i] >= 0)
1527
766
      {
1528
766
        fputc('|', out);
1529
766
        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]);
1530
766
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)62
ccv_array_get62
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
0704
;
1531
766
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 0, flags, out);
1532
766
      } else
1533
35
        fputs("|-", out);
1534
801
    }
1535
605
    fputc('}', out);
1536
605
  }
1537
625
  fputs("\"];\n", out);
1538
625
}
1539
1540
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)
1541
21
{
1542
21
  int i;
1543
21
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1544
21
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"3\" border=\"0\"><b>", out);
1545
0
  else
1546
0
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"2\" border=\"0\"><b>", out);
1547
21
  if (exec_symbol_info->name)
1548
21
    fputs(exec_symbol_info->name, out);
1549
0
  else
1550
0
    fprintf(out, "while%d", index);
1551
21
  fputs(" </b>Command: ", out);
1552
21
  fputs(ccv_nnc_cmd_name(exec_symbol_info->cmd.cmd), out);
1553
21
  fputs("</td></tr>", out);
1554
21
  const int p_idx = while_graph->p_idx - 1;
1555
21
  assert(p_idx >= 0);
1556
21
  if (exec_symbol_info->input_size > 0)
1557
16
  {
1558
16
    fprintf(out, "<tr><td rowspan=\"%d\">Input</td>", exec_symbol_info->input_size);
1559
39
    for (i = 0; i < exec_symbol_info->input_size; 
i++23
)
1560
23
    {
1561
23
      if (i > 0)
1562
7
        fputs("<tr>", out);
1563
23
      if (exec_symbol_info->inputs[i] >= 0)
1564
23
      {
1565
23
        fputs("<td>", out);
1566
23
        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]);
1567
23
        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) : 0;
1568
23
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1569
23
        fputs("</td><td border=\"0\">=&gt; ", out);
1570
23
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
1571
23
        assert(s_idx >= 0);
1572
23
        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);
1573
23
        if (sub_tensor_symbol->name)
1574
21
          fputs(sub_tensor_symbol->name, out);
1575
2
        else
1576
2
          fprintf(out, "tensor%d", s_idx);
1577
23
        fputs("</td></tr>", out);
1578
23
      } else {
1579
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1580
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
1581
0
        else
1582
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1583
0
      }
1584
23
    }
1585
16
  }
1586
21
  if (exec_symbol_info->output_size > 0)
1587
15
  {
1588
15
    fprintf(out, "<tr><td rowspan=\"%d\">Output</td>", exec_symbol_info->output_size);
1589
38
    for (i = 0; i < exec_symbol_info->output_size; 
i++23
)
1590
23
    {
1591
23
      if (i > 0)
1592
8
        fputs("<tr>", out);
1593
23
      if (exec_symbol_info->outputs[i] >= 0)
1594
23
      {
1595
23
        fputs("<td>", out);
1596
23
        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]);
1597
23
        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) : 0;
1598
23
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1599
23
        fputs("</td><td border=\"0\">=&gt; ", out);
1600
23
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
1601
23
        assert(s_idx >= 0);
1602
23
        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);
1603
23
        if (sub_tensor_symbol->name)
1604
22
          fputs(sub_tensor_symbol->name, out);
1605
1
        else
1606
1
          fprintf(out, "tensor%d", s_idx);
1607
23
        fputs("</td></tr>", out);
1608
23
      } else {
1609
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1610
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
1611
0
        else
1612
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1613
0
      }
1614
23
    }
1615
15
  }
1616
127
  
for (i = 0; 21
i < while_graph->tensor_symbol_info->rnum;
i++106
)
1617
106
  {
1618
106
    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);
1619
106
    if (tensor_symbol_info->assign_ref)
1620
24
    {
1621
24
      if (flags == CCV_NNC_LONG_DOT_GRAPH)
1622
24
        fputs("<tr><td colspan=\"3\" border=\"0\">", out);
1623
0
      else
1624
0
        fputs("<tr><td colspan=\"2\" border=\"0\">", out);
1625
24
      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);
1626
24
      if (assign_symbol_info->name)
1627
22
        fputs(assign_symbol_info->name, out);
1628
2
      else
1629
2
        fprintf(out, "tensor%d", tensor_symbol_info->assign_ref - 1);
1630
24
      fputs(" -&gt; ", out);
1631
24
      if (tensor_symbol_info->name)
1632
22
        fputs(tensor_symbol_info->name, out);
1633
2
      else
1634
2
        fprintf(out, "tensor%d", i);
1635
24
      fputs("</td></tr>", out);
1636
24
    }
1637
106
  }
1638
21
  fputs("</table>", out);
1639
21
}
1640
1641
static void _ccv_nnc_symbolic_graph_dot_case_of_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 int flags, FILE* out)
1642
11
{
1643
11
  int i;
1644
11
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1645
11
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"3\" border=\"0\"><b>", out);
1646
0
  else
1647
0
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"2\" border=\"0\"><b>", out);
1648
11
  if (exec_symbol_info->name)
1649
11
    fputs(exec_symbol_info->name, out);
1650
0
  else
1651
0
    fprintf(out, "caseof%d", index);
1652
11
  fputs(" </b>Command: ", out);
1653
11
  fputs(ccv_nnc_cmd_name(exec_symbol_info->cmd.cmd), out);
1654
11
  fputs("</td></tr>", out);
1655
11
  if (exec_symbol_info->input_size > 0)
1656
11
  {
1657
11
    fprintf(out, "<tr><td rowspan=\"%d\">Input</td>", exec_symbol_info->input_size);
1658
38
    for (i = 0; i < exec_symbol_info->input_size; 
i++27
)
1659
27
    {
1660
27
      if (i > 0)
1661
16
        fputs("<tr>", out);
1662
27
      if (exec_symbol_info->inputs[i] >= 0)
1663
27
      {
1664
27
        fputs("<td>", out);
1665
27
        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]);
1666
27
        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) : 0;
1667
27
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1668
27
        fputs("</td></tr>", out);
1669
27
      } else {
1670
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1671
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1672
0
        else
1673
0
          fputs("<td colspan=\"1\">-</td></tr>", out);
1674
0
      }
1675
27
    }
1676
11
  }
1677
11
  if (exec_symbol_info->output_size > 0)
1678
11
  {
1679
11
    fprintf(out, "<tr><td rowspan=\"%d\">Output</td>", exec_symbol_info->output_size);
1680
24
    for (i = 0; i < exec_symbol_info->output_size; 
i++13
)
1681
13
    {
1682
13
      if (i > 0)
1683
2
        fputs("<tr>", out);
1684
13
      if (exec_symbol_info->outputs[i] >= 0)
1685
13
      {
1686
13
        fputs("<td>", out);
1687
13
        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]);
1688
13
        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) : 0;
1689
13
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1690
13
        fputs("</td></tr>", out);
1691
13
      } else {
1692
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1693
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1694
0
        else
1695
0
          fputs("<td colspan=\"1\">-</td></tr>", out);
1696
0
      }
1697
13
    }
1698
11
  }
1699
11
  fputs("</table>", out);
1700
11
}
1701
1702
static void _ccv_nnc_symbolic_graph_dot_sub_graphs(const ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const ccv_array_t* const tensor_symbol_info, const ccv_array_t* const sub_graphs, const int flags, FILE* out, int* c)
1703
32
{
1704
32
  int i, j, k;
1705
32
  // Output this node info within this subgraph.
1706
32
  if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE)
1707
21
  {
1708
21
    fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=<", *c, *c);
1709
21
    const ccv_nnc_symbolic_graph_t* const while_graph = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(sub_graphs, CCV_NNC_GRAPH_REF(exec_symbol_info)[0] - 1);
1710
21
    _ccv_nnc_symbolic_graph_dot_while_label(exec_symbol_info, *c, tensor_symbol_info, while_graph, flags, out);
1711
21
  } else 
if (11
exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF11
) {
1712
11
    fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=<", *c, *c);
1713
11
    _ccv_nnc_symbolic_graph_dot_case_of_label(exec_symbol_info, *c, tensor_symbol_info, flags, out);
1714
11
  }
1715
32
  fputs(">;\n", out);
1716
32
  ++(*c);
1717
81
  for (k = 0; k < exec_symbol_info->graph_ref_size; 
k++49
)
1718
49
  {
1719
49
    if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF)
1720
28
    {
1721
28
      fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=\"\"\n", *c, *c);
1722
28
      ++(*c);
1723
28
    }
1724
49
    const ccv_nnc_symbolic_graph_t* const graph = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(sub_graphs, CCV_NNC_GRAPH_REF(exec_symbol_info)[k] - 1);
1725
49
    int* node_id = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
1726
144
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++95
)
1727
95
    {
1728
95
      node_id[i] = *c;
1729
95
      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);
1730
95
      // Skip the dead one.
1731
95
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1732
95
        
continue2
;
1733
93
      if (exec_symbol_info->graph_ref_size)
1734
3
        _ccv_nnc_symbolic_graph_dot_sub_graphs(exec_symbol_info, graph->tensor_symbol_info, graph->sub_graphs, flags, out, c);
1735
90
      else {
1736
90
        _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, *c, graph->tensor_symbol_info, flags, out);
1737
90
        ++(*c);
1738
90
      }
1739
93
    }
1740
49
    // Output connections.
1741
144
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++95
)
1742
95
    {
1743
95
      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);
1744
95
      // Skip the dead one.
1745
95
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1746
95
        
continue2
;
1747
93
      if (exec_symbol_info->outgoings)
1748
90
        
for (j = 0; 45
j < exec_symbol_info->outgoings->rnum;
j++45
)
1749
45
        {
1750
45
          const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1751
45
          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);
1752
45
          // If both are sub-graphs, have both tail and head specified.
1753
45
          if (CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && 
CCV_NNC_GRAPH_REF1
(outgoing_symbol_info)[0]1
)
1754
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]);
1755
45
          else if (CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && 
!1
CCV_NNC_GRAPH_REF1
(outgoing_symbol_info)[0])
1756
1
            fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1757
44
          else if (!CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && CCV_NNC_GRAPH_REF(outgoing_symbol_info)[0])
1758
3
            fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1759
41
          else
1760
41
            fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1761
45
        }
1762
93
    }
1763
49
    fputs("}\n", out);
1764
49
    ccfree(node_id);
1765
49
  }
1766
32
  // Extra subgraph cluster.
1767
32
  if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF)
1768
11
    fputs("}\n", out);
1769
32
}
1770
1771
void ccv_nnc_symbolic_graph_dot(const ccv_nnc_symbolic_graph_t* const graph, const int flags, FILE* out)
1772
109
{
1773
109
  fputs("digraph G {\ncompound=true;\n", out);
1774
109
  int i, j;
1775
109
  int c = 0;
1776
109
  int* node_id = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
1777
109
  // Output styles.
1778
689
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++580
)
1779
580
  {
1780
580
    node_id[i] = c;
1781
580
    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);
1782
580
    // Skip the dead one.
1783
580
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1784
580
      
continue16
;
1785
564
    if (exec_symbol_info->graph_ref_size)
1786
29
      _ccv_nnc_symbolic_graph_dot_sub_graphs(exec_symbol_info, graph->tensor_symbol_info, graph->sub_graphs, flags, out, &c);
1787
535
    else {
1788
535
      _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, c, graph->tensor_symbol_info, flags, out);
1789
535
      ++c;
1790
535
    }
1791
564
  }
1792
109
  // Output connections.
1793
689
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++580
)
1794
580
  {
1795
580
    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);
1796
580
    // Skip the dead one.
1797
580
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1798
580
      
continue16
;
1799
564
    if (exec_symbol_info->outgoings)
1800
1.08k
      
for (j = 0; 430
j < exec_symbol_info->outgoings->rnum;
j++651
)
1801
651
      {
1802
651
        const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1803
651
        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);
1804
651
        // If both are sub-graphs, have both tail and head specified.
1805
651
        if (exec_symbol_info->graph_ref_size && 
outgoing_symbol_info->graph_ref_size16
)
1806
2
          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]);
1807
649
        else if (exec_symbol_info->graph_ref_size && 
!outgoing_symbol_info->graph_ref_size14
)
1808
14
          fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1809
635
        else if (!exec_symbol_info->graph_ref_size && outgoing_symbol_info->graph_ref_size)
1810
4
          fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1811
631
        else
1812
631
          fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1813
651
      }
1814
564
  }
1815
109
  fputs("}\n", out);
1816
109
  ccfree(node_id);
1817
109
}
1818
1819
void ccv_nnc_symbolic_graph_free(ccv_nnc_symbolic_graph_t* const graph)
1820
198
{
1821
198
  int i;
1822
1.94k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++1.74k
)
1823
1.74k
    _ccv_nnc_graph_exec_symbol_free((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i), 0);
1824
4.39k
  for (i = 0; i < graph->tensor_symbol_info->rnum; 
i++4.19k
)
1825
4.19k
  {
1826
4.19k
    ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i);
1827
4.19k
    if (symbol_info->name)
1828
4.19k
      
ccfree832
(symbol_info->name)832
;
1829
4.19k
    if (symbol_info->s_ref)
1830
74
      ccv_array_free(symbol_info->s_ref);
1831
4.19k
  }
1832
198
  if (graph->sub_graphs)
1833
29
  {
1834
80
    for (i = 0; i < graph->sub_graphs->rnum; 
i++51
)
1835
51
      ccv_nnc_symbolic_graph_free(*(ccv_nnc_symbolic_graph_t**)ccv_array_get(graph->sub_graphs, i));
1836
29
    ccv_array_free(graph->sub_graphs);
1837
29
  }
1838
198
  if (graph->sources)
1839
165
    ccv_array_free(graph->sources);
1840
198
  if (graph->destinations)
1841
165
    ccv_array_free(graph->destinations);
1842
198
  if (graph->breakpoints)
1843
198
    
ccfree33
(graph->breakpoints)33
;
1844
198
  ccv_array_free(graph->tensor_symbol_info);
1845
198
  ccv_array_free(graph->exec_symbol_info);
1846
198
  if (graph->backward.tensor_symbol_idx)
1847
198
    
ccfree58
(graph->backward.tensor_symbol_idx)58
;
1848
198
  if (graph->data_parallel.tensor_symbol_idx)
1849
198
    
ccfree5
(graph->data_parallel.tensor_symbol_idx)5
;
1850
198
  if (graph->data_parallel.exec_symbol_idx)
1851
198
    
ccfree5
(graph->data_parallel.exec_symbol_idx)5
;
1852
198
  ccfree(graph);
1853
198
}
1854
1855
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)
1856
2.28k
{
1857
2.28k
  if (ccv_array_get(symbolic_graph->tensor_symbol_info, 0) != tensor_symbol_info)
1858
2.28k
    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);
1859
2.28k
  if (ccv_array_get(symbolic_graph->exec_symbol_info, 0) != exec_symbol_info)
1860
2.28k
    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);
1861
2.28k
  int i;
1862
2.28k
  if (p_tensor_symbol_info)
1863
417
    
for (i = 0; 64
i < symbolic_graph->tensor_symbol_info->rnum;
i++353
)
1864
353
      if (tensor_symbol_info[i].p_ref)
1865
132
      {
1866
132
        const int p_ref = tensor_symbol_info[i].p_ref - 1;
1867
132
        assert(p_ref < p_tensor_symbol_info_size);
1868
132
        tensor_symbol_info[i].info = p_tensor_symbol_info[p_ref].info;
1869
132
        // I don't need to copy over inc and ofs for alias.
1870
132
      }
1871
2.28k
  int max_input_size = 0, max_output_size = 0;
1872
2.28k
  // Materialize auto hints.
1873
16.9k
  for (i = 0; i < symbolic_graph->exec_symbol_info->rnum; 
i++14.6k
)
1874
14.6k
  {
1875
14.6k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info[i].flags))
1876
14.6k
      
continue6
;
1877
14.6k
    max_input_size = ccv_max(max_input_size, exec_symbol_info[i].input_size);
1878
14.6k
    max_output_size = ccv_max(max_output_size, exec_symbol_info[i].output_size);
1879
14.6k
    // If there is no hint and we have input and output tensor specified.
1880
14.6k
    if (ccv_nnc_is_no_hint(exec_symbol_info[i].hint) &&
1881
14.6k
      
exec_symbol_info[i].input_size > 013.6k
&&
exec_symbol_info[i].inputs[0] >= 012.4k
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].inputs[0]].info)12.4k
&&
1882
14.6k
      
exec_symbol_info[i].output_size > 012.4k
&&
exec_symbol_info[i].outputs[0] >= 012.4k
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].outputs[0]].info)12.4k
)
1883
12.4k
      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);
1884
14.6k
  }
1885
2.28k
1886
2.28k
  ccv_nnc_tensor_param_t input_params[ccv_max(1, max_input_size)];
1887
2.28k
  ccv_nnc_tensor_param_t output_params[ccv_max(1, max_output_size)];
1888
2.28k
1889
2.28k
  // Materialize auto tensors. This need to go with the topological order.
1890
2.28k
  // TODO: Need to proper handle sub-graphs (thus, run sub-graph to figure out the tensor properties).
1891
11.5k
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node) {
1892
11.5k
    if (node->input_size > 0 && 
node->output_size > 010.3k
)
1893
10.3k
    {
1894
40.6k
      for (i = 0; i < node->input_size; 
i++30.3k
)
1895
30.3k
        input_params[i] = node->inputs[i] >= 0 ? 
tensor_symbol_info[node->inputs[i]].info24.8k
:
ccv_nnc_tensor_auto5.48k
;
1896
10.3k
      ccv_nnc_hint_tensor_auto(node->cmd, input_params, node->input_size, node->hint, output_params, node->output_size);
1897
26.5k
      for (i = 0; i < node->output_size; 
i++16.1k
)
1898
16.1k
        /* Only assign the output parameters if the symbol itself is auto. */
1899
16.1k
        if (node->outputs[i] >= 0 && 
ccv_nnc_is_tensor_auto(tensor_symbol_info[node->outputs[i]].info)14.9k
)
1900
28
          tensor_symbol_info[node->outputs[i]].info = output_params[i];
1901
10.3k
    }
1902
11.5k
  } ccv_nnc_graph_visit_endfor
1903
2.28k
  // If still point to any device, assign default device 00 to it.
1904
30.7k
  for (i = 0; i < symbolic_graph->tensor_symbol_info->rnum; 
i++28.4k
)
1905
28.4k
    if (CCV_TENSOR_GET_DEVICE(tensor_symbol_info[i].info.type) == CCV_COMPUTE_DEVICE_ANY)
1906
16.7k
      tensor_symbol_info[i].info.type = (~CCV_COMPUTE_DEVICE_ANY & tensor_symbol_info[i].info.type) | CCV_COMPUTE_DEVICE_000;
1907
2.28k
}