Coverage Report

Created: 2022-08-03 23:52

/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
// 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
690k
{
13
690k
  return (memcmp(&params, &ccv_nnc_tensor_auto, sizeof(ccv_nnc_tensor_param_t)) == 0);
14
690k
}
15
16
ccv_nnc_symbolic_graph_t* ccv_nnc_symbolic_graph_new(void)
17
2.51k
{
18
2.51k
  ccv_nnc_symbolic_graph_t* graph = cccalloc(1, sizeof(ccv_nnc_symbolic_graph_t));
19
2.51k
  graph->tensor_symbol_info = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_info_t), 5, 0);
20
2.51k
  graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), 5, 0);
21
2.51k
  graph->reuse.exec = -1;
22
2.51k
  graph->reuse.tensor = -1;
23
2.51k
  return graph;
24
2.51k
}
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 len = strnlen(name, 63);
41
70
      const size_t n = len + 1;
42
70
      symbol_info->name = (char*)ccmalloc(n);
43
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
44
70
      memcpy(symbol_info->name, name, n);
45
70
      symbol_info->name[len] = 0;
46
70
    }
47
78
    if (symbol_info->s_ref)
48
6
    {
49
6
      ccv_array_t* const s_ref = symbol_info->s_ref;
50
6
      symbol_info->s_ref = ccv_array_new(sizeof(int), s_ref->rnum, 0);
51
6
      symbol_info->s_ref->rnum = s_ref->rnum;
52
6
      memcpy(ccv_array_get(symbol_info->s_ref, 0), ccv_array_get(s_ref, 0), sizeof(int) * s_ref->rnum);
53
6
    }
54
78
  }
55
13
  new_graph->exec_symbol_info = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_info_t), graph->exec_symbol_info->rnum, 0);
56
13
  new_graph->exec_symbol_info->rnum = graph->exec_symbol_info->rnum;
57
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);
58
48
  for (i = 0; i < new_graph->exec_symbol_info->rnum; 
i++35
)
59
35
  {
60
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);
61
35
    if (symbol_info->name)
62
25
    {
63
25
      char* const name = symbol_info->name;
64
25
      const size_t len = strnlen(name, 63);
65
25
      const size_t n = len + 1;
66
25
      symbol_info->name = (char*)ccmalloc(n);
67
      // Don't use strndup because this way I can have custom allocator (for ccmalloc).
68
25
      memcpy(symbol_info->name, name, n);
69
25
      symbol_info->name[len] = 0;
70
25
    }
71
35
    if (symbol_info->outgoings)
72
20
    {
73
20
      ccv_array_t* const outgoings = symbol_info->outgoings;
74
20
      symbol_info->outgoings = ccv_array_new(sizeof(int), outgoings->rnum, 0);
75
20
      symbol_info->outgoings->rnum = outgoings->rnum;
76
20
      memcpy(ccv_array_get(symbol_info->outgoings, 0), ccv_array_get(outgoings, 0), sizeof(int) * outgoings->rnum);
77
20
    }
78
35
    if (symbol_info->inputs)
79
22
    {
80
22
      int* const inputs = symbol_info->inputs;
81
22
      symbol_info->inputs = (int*)ccmalloc(sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
82
22
      symbol_info->outputs = symbol_info->inputs + symbol_info->input_size;
83
22
      memcpy(symbol_info->inputs, inputs, sizeof(int) * (symbol_info->input_size + symbol_info->output_size));
84
22
    }
85
35
    if (symbol_info->_heap_graph_ref)
86
2
    {
87
2
      int* const heap_graph_ref = symbol_info->_heap_graph_ref;
88
2
      symbol_info->_heap_graph_ref = (int*)ccmalloc(sizeof(int) * symbol_info->graph_ref_size);
89
2
      memcpy(symbol_info->_heap_graph_ref, heap_graph_ref, sizeof(int) * symbol_info->graph_ref_size);
90
2
    }
91
35
    if ((symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE) && 
symbol_info->input_size > 01
)
92
1
    {
93
1
      int* const inputs = symbol_info->p_while.inputs;
94
1
      symbol_info->p_while.inputs = (int*)ccmalloc(sizeof(int) * symbol_info->p_while.input_size);
95
1
      memcpy(symbol_info->p_while.inputs, inputs, sizeof(int) * symbol_info->p_while.input_size);
96
1
    }
97
35
  }
98
13
  if (graph->sources)
99
13
  {
100
13
    new_graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->sources->rnum, 0);
101
13
    new_graph->sources->rnum = graph->sources->rnum;
102
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);
103
26
    for (i = 0; i < new_graph->sources->rnum; 
i++13
)
104
13
      ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(new_graph->sources, i))->graph = new_graph;
105
13
  }
106
13
  if (graph->destinations)
107
13
  {
108
13
    new_graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), graph->destinations->rnum, 0);
109
13
    new_graph->destinations->rnum = graph->destinations->rnum;
110
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);
111
26
    for (i = 0; i < new_graph->destinations->rnum; 
i++13
)
112
13
      ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(new_graph->destinations, i))->graph = new_graph;
113
13
  }
114
13
  if (graph->breakpoints)
115
13
  {
116
13
    new_graph->breakpoints = (ccv_nnc_graph_exec_symbol_t*)ccmalloc(sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
117
13
    memcpy(new_graph->breakpoints, graph->breakpoints, sizeof(ccv_nnc_graph_exec_symbol_t) * graph->breakpoint_size);
118
26
    for (i = 0; i < graph->breakpoint_size; 
i++13
)
119
13
      new_graph->breakpoints[i].graph = new_graph;
120
13
  }
121
13
  if (graph->backward.tensor_symbol_idx)
122
1
  {
123
1
    new_graph->backward.tensor_symbol_idx = (int*)ccmalloc(sizeof(int) * (new_graph->backward.tensor_symbol_size + new_graph->backward.exec_symbol_size));
124
1
    if (new_graph->backward.tensor_symbol_size > 0)
125
1
      memcpy(new_graph->backward.tensor_symbol_idx, graph->backward.tensor_symbol_idx, sizeof(int) * new_graph->backward.tensor_symbol_size);
126
1
    new_graph->backward.exec_symbol_idx = new_graph->backward.tensor_symbol_idx + new_graph->backward.tensor_symbol_size;
127
1
    if (new_graph->backward.exec_symbol_size > 0)
128
1
      memcpy(new_graph->backward.exec_symbol_idx, graph->backward.exec_symbol_idx, sizeof(int) * new_graph->backward.exec_symbol_size);
129
1
  }
130
13
  if (subst)
131
13
  {
132
48
    for (i = 0; i < new_graph->exec_symbol_info->rnum; 
i++35
)
133
35
    {
134
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);
135
35
      if (!CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
136
33
      {
137
33
        symbol_info->cmd = subst((ccv_nnc_graph_exec_symbol_t){
138
33
          .d = i,
139
33
          .graph = graph,
140
33
        }, symbol_info->cmd);
141
33
        if (symbol_info->cmd.cmd != CCV_NNC_GRAPH_FORWARD && symbol_info->cmd.cmd != CCV_NNC_GRAPH_BACKWARD)
142
33
        {
143
33
          symbol_info->graph_ref_size = 0;
144
33
          if (symbol_info->_heap_graph_ref)
145
2
          {
146
2
            ccfree(symbol_info->_heap_graph_ref);
147
2
            symbol_info->_heap_graph_ref = 0;
148
2
          }
149
33
        }
150
33
      }
151
35
    }
152
13
  }
153
  // TODO: See how and if I need to dup sub-graphs. I also need to figure out what's the relationship between this graph
154
  // and its parent graph (or how can we use the symbol from the graph properly).
155
13
  new_graph->sub_graphs = 0;
156
13
  return new_graph;
157
13
}
158
159
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)
160
101k
{
161
101k
  ccv_nnc_tensor_symbol_t symbol = {
162
101k
    .d = graph->tensor_symbol_info->rnum,
163
101k
    .graph = graph
164
101k
  };
165
101k
  ccv_nnc_tensor_symbol_info_t symbol_info = {
166
101k
    .info = info,
167
101k
  };
168
101k
  if (name)
169
890
  {
170
890
    const size_t len = strnlen(name, 63);
171
890
    const size_t n = len + 1;
172
890
    symbol_info.name = (char*)ccmalloc(n);
173
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
174
890
    memcpy(symbol_info.name, name, n);
175
890
    symbol_info.name[len] = 0;
176
890
  }
177
101k
  if (graph->reuse.tensor >= 0)
178
16.2k
  {
179
16.2k
    const int reuse_tensor_d = graph->reuse.tensor;
180
16.2k
    assert(reuse_tensor_d < graph->tensor_symbol_info->rnum);
181
16.2k
    *(ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, reuse_tensor_d) = symbol_info;
182
16.2k
    int i;
183
16.2k
    graph->reuse.tensor = -1;
184
29.9k
    for (i = reuse_tensor_d + 1; i < graph->tensor_symbol_info->rnum && 
graph->reuse.tensor < 022.5k
;
i++13.7k
)
185
13.7k
      if (CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
186
11.4k
        graph->reuse.tensor = i;
187
16.2k
    symbol.d = reuse_tensor_d;
188
16.2k
  } else
189
84.9k
    ccv_array_push(graph->tensor_symbol_info, &symbol_info);
190
101k
  if (graph->hooks.tensor_symbol_new.func)
191
48.9k
    graph->hooks.tensor_symbol_new.func(graph->hooks.tensor_symbol_new.context, symbol, info, name);
192
101k
  return symbol;
193
101k
}
194
195
void* ccv_nnc_tensor_symbol_new_hook(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_tensor_symbol_new_hook_f hook, void* context)
196
11.6k
{
197
11.6k
  void* const prev = graph->hooks.tensor_symbol_new.context;
198
11.6k
  graph->hooks.tensor_symbol_new.func = hook;
199
11.6k
  graph->hooks.tensor_symbol_new.context = context;
200
11.6k
  return prev;
201
11.6k
}
202
203
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)
204
4.01k
{
205
4.01k
  assert(tensor_symbol.graph == graph);
206
4.01k
  int d = tensor_symbol.d;
207
4.01k
  assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
208
4.01k
  ccv_nnc_tensor_symbol_info_t* info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
209
  // Find the root tensor that is not an alias.
210
4.01k
  while (info_d->alias_ref)
211
0
  {
212
0
    d = info_d->alias_ref - 1;
213
0
    assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
214
0
    info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
215
0
  }
216
4.01k
  ccv_nnc_tensor_symbol_t alias = {
217
4.01k
    .d = graph->tensor_symbol_info->rnum,
218
4.01k
    .graph = graph
219
4.01k
  };
220
  // Alias comes in two shapes: the total tensor count is strictly smaller or equal to.
221
  // If it is not auto, check dimensions.
222
4.01k
  if (!ccv_nnc_is_tensor_auto(info_d->info))
223
4.01k
    { assert(ccv_nnc_dimension_count(inc) <= ccv_nnc_tensor_count(info_d->info)); }
224
4.01k
  ccv_nnc_tensor_symbol_info_t alias_info = {
225
4.01k
    .alias_ref = d + 1,
226
4.01k
    .info = info,
227
4.01k
  };
228
4.01k
  if (name)
229
68
  {
230
68
    const size_t len = strnlen(name, 63);
231
68
    const size_t n = len + 1;
232
68
    alias_info.name = (char*)ccmalloc(n);
233
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
234
68
    memcpy(alias_info.name, name, n);
235
68
    alias_info.name[len] = 0;
236
68
  }
237
4.01k
  memcpy(alias_info.ofs, ofs, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
238
4.01k
  memcpy(alias_info.inc, inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
239
4.01k
  if (graph->reuse.tensor >= 0)
240
6
  {
241
6
    const int reuse_tensor_d = graph->reuse.tensor;
242
6
    *(ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, reuse_tensor_d) = alias_info;
243
6
    int i;
244
6
    graph->reuse.tensor = -1;
245
12
    for (i = reuse_tensor_d + 1; i < graph->tensor_symbol_info->rnum && graph->reuse.tensor < 0; 
i++6
)
246
6
      if (CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
247
6
        graph->reuse.tensor = i;
248
6
    alias.d = reuse_tensor_d;
249
6
  } else
250
4.01k
    ccv_array_push(graph->tensor_symbol_info, &alias_info);
251
4.01k
  if (graph->hooks.tensor_symbol_alias_new.func)
252
1.49k
    graph->hooks.tensor_symbol_alias_new.func(graph->hooks.tensor_symbol_alias_new.context, alias, tensor_symbol, ofs, inc, info, name);
253
4.01k
  return alias;
254
4.01k
}
255
256
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)
257
11.6k
{
258
11.6k
  void* const prev = graph->hooks.tensor_symbol_alias_new.context;
259
11.6k
  graph->hooks.tensor_symbol_alias_new.func = hook;
260
11.6k
  graph->hooks.tensor_symbol_alias_new.context = context;
261
11.6k
  return prev;
262
11.6k
}
263
264
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)
265
99.1k
{
266
99.1k
  assert(tensor_symbol.graph == graph);
267
99.1k
  int d = tensor_symbol.d;
268
99.1k
  assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
269
99.1k
  ccv_nnc_tensor_symbol_info_t* info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
270
  // Find the root tensor that is not an alias.
271
102k
  while (info_d->alias_ref)
272
3.04k
  {
273
3.04k
    d = info_d->alias_ref - 1;
274
3.04k
    assert(d >= 0 && d < graph->tensor_symbol_info->rnum);
275
3.04k
    info_d = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
276
3.04k
  }
277
99.1k
  if (d != tensor_symbol.d)
278
3.04k
    return (ccv_nnc_tensor_symbol_t){
279
3.04k
      .d = d,
280
3.04k
      .graph = graph
281
3.04k
    };
282
96.0k
  return (ccv_nnc_tensor_symbol_t){
283
96.0k
    .d = CCV_NNC_NO_TENSOR_SYMBOL,
284
96.0k
    .graph = 0
285
96.0k
  };
286
99.1k
}
287
288
// Resolve this tensor symbol to the current graph. If cannot find, return no symbol.
289
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)
290
190k
{
291
190k
  if (graph == tensor_symbol.graph)
292
190k
    return tensor_symbol;
293
38
  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);
294
38
  assert(!symbol_info->alias_ref);
295
  // Find if the symbol is in the sub-graph.
296
38
  const ccv_nnc_symbolic_graph_t* curr_graph = tensor_symbol.graph;
297
38
  assert(tensor_symbol.d >= 0 && tensor_symbol.d < curr_graph->tensor_symbol_info->rnum);
298
78
  
while (38
curr_graph &&
curr_graph != graph57
)
299
40
    curr_graph = curr_graph->p;
300
38
  if (curr_graph)
301
17
  {
302
    // The graph is a parent of the symbol passed in.
303
17
    curr_graph = tensor_symbol.graph;
304
17
    ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
305
17
    ccv_nnc_tensor_symbol_t curr_symbol = tensor_symbol;
306
22
    while (curr_graph != graph)
307
17
    {
308
17
      ccv_nnc_symbolic_graph_t* const p = curr_graph->p;
309
      // Cannot find the relevant one in the parent graph, return no symbol.
310
17
      if (!curr_symbol_info->p_ref)
311
12
        return (ccv_nnc_tensor_symbol_t){
312
12
          .d = CCV_NNC_NO_TENSOR_SYMBOL,
313
12
          .graph = graph,
314
12
        };
315
5
      curr_symbol.d = curr_symbol_info->p_ref - 1;
316
5
      curr_symbol.graph = p;
317
5
      assert(curr_symbol.d >= 0 && curr_symbol.d < p->tensor_symbol_info->rnum);
318
5
      curr_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, curr_symbol.d);
319
      // Move on.
320
5
      curr_graph = p;
321
5
    }
322
5
    return curr_symbol;
323
17
  }
324
  // Otherwise, if the symbol is in the parent graph, this is a bit more expensive because I need to keep a trace stack.
325
21
  curr_graph = graph;
326
21
  int d;
327
42
  for (d = 0; curr_graph && curr_graph != tensor_symbol.graph; 
d++21
)
328
21
    curr_graph = curr_graph->p;
329
21
  curr_graph = graph;
330
21
  assert(d > 0);
331
21
  int trace[d];
332
42
  for (d = 0; curr_graph && curr_graph != tensor_symbol.graph; 
d++21
)
333
21
  {
334
21
    const int p_idx = curr_graph->p_idx - 1;
335
21
    trace[d] = p_idx;
336
21
    curr_graph = curr_graph->p;
337
21
  }
338
  // If it is not in both the parent graph and the sub-graph, the input is invalid.
339
21
  assert(curr_graph);
340
21
  curr_graph = tensor_symbol.graph;
341
21
  ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
342
21
  ccv_nnc_tensor_symbol_t curr_symbol = tensor_symbol;
343
  // The graph is a sub graph of the symbol passed in.
344
21
  int i;
345
42
  for (i = d - 1; i >= 0; 
i--21
)
346
21
  {
347
21
    const int p_idx = trace[i];
348
21
    assert(p_idx >= 0);
349
    // Cannot find the relevant one in the sub-graph, return no symbol.
350
21
    if (!curr_graph->sub_graphs || !curr_symbol_info->s_ref ||
351
21
      curr_symbol_info->s_ref->rnum != curr_graph->sub_graphs->rnum)
352
0
        return (ccv_nnc_tensor_symbol_t){
353
0
          .d = CCV_NNC_NO_TENSOR_SYMBOL,
354
0
          .graph = graph,
355
0
        };
356
21
    assert(p_idx >= 0 && p_idx < curr_symbol_info->s_ref->rnum);
357
21
    const int s_idx = *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx);
358
21
    ccv_nnc_symbolic_graph_t* const s = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(curr_graph->sub_graphs, p_idx);
359
21
    curr_symbol.d = s_idx - 1;
360
21
    curr_symbol.graph = s;
361
21
    assert(curr_symbol.d >= 0 && curr_symbol.d < s->tensor_symbol_info->rnum);
362
21
    curr_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, curr_symbol.d);
363
    // Move on.
364
21
    curr_graph = s;
365
21
  }
366
21
  return curr_symbol;
367
21
}
368
369
// This method generate tensor symbols and their links along the way when traverse the graph.
370
enum {
371
  MAP_TENSOR_USE_AS_INPUT,
372
  MAP_TENSOR_USE_AS_OUTPUT,
373
};
374
375
static void _ccv_nnc_graph_exec_add_input_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
376
52
{
377
52
  int i;
378
83
  for (i = 0; i < exec_symbol_info->input_size; 
i++31
)
379
53
    if (exec_symbol_info->inputs[i] == d)
380
22
      return; // No need to continue, this symbol already exists as input.
381
  // Expand the array.
382
30
  if (!exec_symbol_info->input_size && 
!exec_symbol_info->output_size16
)
383
16
  {
384
16
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
385
16
    exec_symbol_info->inputs[0] = d;
386
16
    exec_symbol_info->input_size = 1;
387
16
    exec_symbol_info->outputs = exec_symbol_info->inputs + 1;
388
16
    return;
389
16
  }
390
14
  exec_symbol_info->inputs = (int*)ccrealloc(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + 1 + exec_symbol_info->output_size));
391
14
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
392
14
  if (exec_symbol_info->output_size)
393
6
    memmove(exec_symbol_info->outputs + 1, exec_symbol_info->outputs, sizeof(int) * exec_symbol_info->output_size); 
394
14
  exec_symbol_info->inputs[exec_symbol_info->input_size] = d;
395
14
  ++exec_symbol_info->input_size;
396
14
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
397
14
}
398
399
static void _ccv_nnc_graph_exec_add_output_if_needed(ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info, const int d)
400
52
{
401
52
  int i;
402
69
  for (i = 0; i < exec_symbol_info->output_size; 
i++17
)
403
45
    if (exec_symbol_info->outputs[i] == d)
404
28
      return; // No need to continue, this symbol already exists as output.
405
  // Expand the array.
406
24
  if (!exec_symbol_info->input_size && 
!exec_symbol_info->output_size7
)
407
3
  {
408
3
    exec_symbol_info->inputs = (int*)ccmalloc(sizeof(int));
409
3
    exec_symbol_info->outputs = exec_symbol_info->inputs;
410
3
    exec_symbol_info->outputs[0] = d;
411
3
    exec_symbol_info->output_size = 1;
412
3
    return;
413
3
  }
414
21
  exec_symbol_info->inputs = (int*)ccrealloc(exec_symbol_info->inputs, sizeof(int) * (exec_symbol_info->input_size + exec_symbol_info->output_size + 1));
415
21
  exec_symbol_info->outputs = exec_symbol_info->inputs + exec_symbol_info->input_size;
416
21
  exec_symbol_info->outputs[exec_symbol_info->output_size] = d;
417
21
  ++exec_symbol_info->output_size;
418
21
}
419
420
void ccv_nnc_tensor_symbol_pair_with(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor_symbol, const ccv_nnc_tensor_symbol_t pair_tensor_symbol)
421
11
{
422
11
  assert(tensor_symbol.graph == graph);
423
11
  assert(tensor_symbol.d >= 0);
424
11
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
425
11
  assert(pair_tensor_symbol.graph == graph->pair);
426
11
  assert(pair_tensor_symbol.d >= 0);
427
11
  assert(pair_tensor_symbol.d < graph->pair->tensor_symbol_info->rnum);
428
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);
429
11
  tensor_info->pair_ref = pair_tensor_symbol.d + 1;
430
11
}
431
432
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)
433
68
{
434
68
  assert(graph && symbol.graph);
435
68
  assert(symbol.graph != graph);
436
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);
437
68
  assert(!symbol_info->alias_ref);
438
  // Find if the symbol is in the sub-graph.
439
68
  const ccv_nnc_symbolic_graph_t* curr_graph = symbol.graph;
440
68
  assert(symbol.d >= 0 && symbol.d < curr_graph->tensor_symbol_info->rnum);
441
139
  
while (68
curr_graph &&
curr_graph != graph90
)
442
71
    curr_graph = curr_graph->p;
443
68
  if (curr_graph)
444
19
  {
445
    // The graph is a parent of the symbol passed in. For this case, if we are connecting this symbol to an exec as input,
446
    // that means it must be an output in these sub-graphs. Otherwise, if we are connecting this symbol to an exec as output,
447
    // it must be an input in these sub-graphs.
448
19
    curr_graph = symbol.graph;
449
19
    ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
450
19
    ccv_nnc_tensor_symbol_t curr_symbol = symbol;
451
38
    while (curr_graph != graph)
452
19
    {
453
19
      ccv_nnc_symbolic_graph_t* const p = curr_graph->p;
454
      // I need to find the symbol whether it exists or not before creating new one.
455
19
      ccv_nnc_tensor_symbol_t new_symbol;
456
19
      ccv_nnc_tensor_symbol_info_t* new_symbol_info;
457
19
      if (!curr_symbol_info->p_ref)
458
18
      {
459
18
        new_symbol = ccv_nnc_tensor_symbol_new(p, curr_symbol_info->info, curr_symbol_info->name);
460
18
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
461
18
        curr_symbol_info->p_ref = new_symbol.d + 1;
462
18
        new_symbol_info->s_ref = ccv_array_new(sizeof(int), p->sub_graphs->rnum, 0);
463
18
        new_symbol_info->s_ref->rnum = p->sub_graphs->rnum;
464
18
        ccv_array_zero(new_symbol_info->s_ref);
465
18
        *(int*)ccv_array_get(new_symbol_info->s_ref, curr_graph->p_idx - 1) = curr_symbol.d + 1;
466
18
      } else {
467
1
        new_symbol.d = curr_symbol_info->p_ref - 1;
468
1
        new_symbol.graph = p;
469
1
        assert(new_symbol.d >= 0 && new_symbol.d < p->tensor_symbol_info->rnum);
470
1
        new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(p->tensor_symbol_info, new_symbol.d);
471
1
      }
472
19
      if (curr_graph->exec_idx)
473
19
      {
474
        // This is a sub-graph.
475
19
        assert(p);
476
19
        assert(curr_graph->exec_idx > 0 && curr_graph->exec_idx <= p->exec_symbol_info->rnum);
477
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);
478
19
        switch (map_use)
479
19
        {
480
19
          case MAP_TENSOR_USE_AS_INPUT:
481
19
            _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, new_symbol.d);
482
19
            break;
483
0
          case MAP_TENSOR_USE_AS_OUTPUT:
484
0
            _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, new_symbol.d);
485
0
            break;
486
19
        }
487
19
      }
488
      // Move on.
489
19
      curr_symbol = new_symbol;
490
19
      curr_symbol_info = new_symbol_info;
491
19
      curr_graph = p;
492
19
    }
493
19
    return curr_symbol.d;
494
19
  }
495
  // Otherwise, if the symbol is in the parent graph, this is a bit more expensive because I need to keep a trace stack.
496
49
  curr_graph = graph;
497
49
  int d;
498
99
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++50
)
499
50
    curr_graph = curr_graph->p;
500
49
  curr_graph = graph;
501
49
  assert(d > 0);
502
49
  int trace[d];
503
99
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++50
)
504
50
  {
505
50
    const int p_idx = curr_graph->p_idx - 1;
506
50
    trace[d] = p_idx;
507
50
    curr_graph = curr_graph->p;
508
50
  }
509
  // If it is not in both the parent graph and the sub-graph, the input is invalid.
510
49
  assert(curr_graph);
511
49
  curr_graph = symbol.graph;
512
49
  ccv_nnc_tensor_symbol_info_t* curr_symbol_info = symbol_info;
513
49
  ccv_nnc_tensor_symbol_t curr_symbol = symbol;
514
  // 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,
515
  // that means it must be an input in these parent graphs. Otherwise, if we are connecting this symbol to an exec as output,
516
  // it must be an output in these parent graphs.
517
49
  int i;
518
99
  for (i = d - 1; i >= 0; 
i--50
)
519
50
  {
520
50
    const int p_idx = trace[i];
521
50
    assert(p_idx >= 0);
522
50
    assert(curr_graph->sub_graphs);
523
50
    if (!curr_symbol_info->s_ref)
524
36
    {
525
36
      curr_symbol_info->s_ref = ccv_array_new(sizeof(int), curr_graph->sub_graphs->rnum, 0);
526
36
      curr_symbol_info->s_ref->rnum = curr_graph->sub_graphs->rnum;
527
36
      ccv_array_zero(curr_symbol_info->s_ref);
528
36
    } else 
if (14
curr_symbol_info->s_ref->rnum != curr_graph->sub_graphs->rnum14
)
529
8
      ccv_array_resize(curr_symbol_info->s_ref, curr_graph->sub_graphs->rnum);
530
50
    assert(p_idx >= 0 && p_idx < curr_symbol_info->s_ref->rnum);
531
50
    const int s_idx = *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx);
532
50
    ccv_nnc_symbolic_graph_t* const s = *(ccv_nnc_symbolic_graph_t**)ccv_array_get(curr_graph->sub_graphs, p_idx);
533
50
    ccv_nnc_tensor_symbol_t new_symbol;
534
50
    ccv_nnc_tensor_symbol_info_t* new_symbol_info;
535
    // I need to find the symbol whether it exists or not before creating new one.
536
50
    if (!s_idx)
537
44
    {
538
44
      new_symbol = ccv_nnc_tensor_symbol_new(s, symbol_info->info, symbol_info->name);
539
44
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
540
44
      new_symbol_info->p_ref = curr_symbol.d + 1;
541
44
      *(int*)ccv_array_get(curr_symbol_info->s_ref, p_idx) = new_symbol.d + 1;
542
44
    } else {
543
6
      new_symbol.d = s_idx - 1;
544
6
      new_symbol.graph = s;
545
6
      assert(new_symbol.d >= 0 && new_symbol.d < s->tensor_symbol_info->rnum);
546
6
      new_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(s->tensor_symbol_info, new_symbol.d);
547
6
    }
548
50
    if (s->exec_idx)
549
50
    {
550
50
      assert(s->p); // This is a sub-graph.
551
50
      assert(s->exec_idx > 0 && s->exec_idx <= curr_graph->exec_symbol_info->rnum);
552
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);
553
50
      switch (map_use)
554
50
      {
555
45
        case MAP_TENSOR_USE_AS_INPUT:
556
45
          _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, curr_symbol.d);
557
45
          break;
558
5
        case MAP_TENSOR_USE_AS_OUTPUT:
559
5
          _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, curr_symbol.d);
560
5
          break;
561
50
      }
562
50
    }
563
    // Move on.
564
50
    curr_symbol = new_symbol;
565
50
    curr_symbol_info = new_symbol_info;
566
50
    curr_graph = s;
567
50
  }
568
49
  return curr_symbol.d;
569
49
}
570
571
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)
572
68
{
573
68
  assert(graph && symbol.graph);
574
68
  assert(symbol.graph != graph);
575
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);
576
68
  if (!symbol_info->alias_ref)
577
62
    return _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, symbol, map_use);
578
6
  const int d = symbol_info->alias_ref - 1;
579
6
  assert(d >= 0 && d < symbol.graph->tensor_symbol_info->rnum);
580
6
  const int map_d = _ccv_nnc_symbolic_graph_map_tensor_symbol_no_alias(graph, (ccv_nnc_tensor_symbol_t){
581
6
    .graph = symbol.graph,
582
6
    .d = d
583
6
  }, map_use);
584
6
  const ccv_nnc_tensor_symbol_t alias = ccv_nnc_tensor_symbol_alias_new(graph, (ccv_nnc_tensor_symbol_t){
585
6
    .graph = graph,
586
6
    .d = map_d
587
6
  }, symbol_info->ofs, symbol_info->inc, symbol_info->info, symbol_info->name);
588
6
  return alias.d;
589
6
}
590
591
int ccv_nnc_tensor_symbol_map_raw(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t symbol)
592
143k
{
593
143k
  if (symbol.d >= 0)
594
115k
    return symbol.graph != graph ? 
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, symbol, MAP_TENSOR_USE_AS_INPUT)61
:
symbol.d115k
;
595
27.7k
  if (symbol.graph == graph || 
symbol.d == CCV_NNC_NO_TENSOR_SYMBOL27.7k
)
596
27.7k
    return symbol.d;
597
1
  ccv_nnc_symbolic_graph_t* curr_graph = graph;
598
1
  int d;
599
2
  for (d = 0; curr_graph && curr_graph != symbol.graph; 
d++1
)
600
1
    curr_graph = curr_graph->p;
601
1
  assert(curr_graph == symbol.graph);
602
1
  return CCV_NNC_ENCODE_WHILE_COUNT_SYMBOL(d);
603
1
}
604
605
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)
606
35
{
607
35
  assert(src_graph != dest_graph);
608
35
  assert(src_graph->p == dest_graph || dest_graph->p == src_graph);
609
35
  assert(src_tensor_symbol.d >= 0);
610
35
  assert(dest_tensor_symbol.d >= 0);
611
35
  ccv_nnc_tensor_symbol_t tensor_symbol = src_tensor_symbol;
612
35
  if (tensor_symbol.graph != src_graph)
613
2
    tensor_symbol = (ccv_nnc_tensor_symbol_t){
614
2
      .graph = src_graph,
615
2
      .d = _ccv_nnc_symbolic_graph_map_tensor_symbol(src_graph, tensor_symbol, MAP_TENSOR_USE_AS_INPUT),
616
2
    };
617
35
  ccv_nnc_tensor_symbol_t sub_tensor_symbol = dest_tensor_symbol;
618
35
  if (sub_tensor_symbol.graph != dest_graph)
619
0
    sub_tensor_symbol = (ccv_nnc_tensor_symbol_t){
620
0
      .graph = dest_graph,
621
0
      .d = _ccv_nnc_symbolic_graph_map_tensor_symbol(dest_graph, sub_tensor_symbol, MAP_TENSOR_USE_AS_OUTPUT),
622
0
    };
623
35
  ccv_nnc_symbolic_graph_t* curr_graph = src_graph;
624
70
  while (curr_graph && 
curr_graph != dest_graph63
)
625
35
    curr_graph = curr_graph->p;
626
35
  ccv_nnc_symbolic_graph_t* graph;
627
35
  ccv_nnc_symbolic_graph_t* sub_graph;
628
35
  int map_use;
629
35
  if (curr_graph)
630
28
  {
631
    // src_graph is the sub graph, dest_graph is the parent graph.
632
28
    graph = dest_graph;
633
28
    sub_graph = src_graph;
634
    // Swap tensor_symbol and sub_tensor_symbol
635
28
    ccv_nnc_tensor_symbol_t x;
636
28
    CCV_SWAP(tensor_symbol, sub_tensor_symbol, x);
637
28
    map_use = MAP_TENSOR_USE_AS_OUTPUT;
638
28
  } else {
639
7
    graph = src_graph;
640
7
    sub_graph = dest_graph;
641
7
    map_use = MAP_TENSOR_USE_AS_INPUT;
642
7
  }
643
35
  ccv_nnc_symbolic_graph_t* p_graph = sub_graph;
644
35
  while (p_graph && p_graph->p != graph)
645
0
    p_graph = p_graph->p;
646
35
  assert(p_graph);
647
35
  if (p_graph != sub_graph)
648
0
  {
649
0
    sub_tensor_symbol.d = _ccv_nnc_symbolic_graph_map_tensor_symbol(p_graph, sub_tensor_symbol, map_use);
650
0
    sub_tensor_symbol.graph = p_graph;
651
0
    sub_graph = p_graph;
652
0
  }
653
35
  assert(tensor_symbol.d < graph->tensor_symbol_info->rnum);
654
35
  assert(sub_tensor_symbol.d < sub_graph->tensor_symbol_info->rnum);
655
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);
656
35
  sub_tensor_info->p_ref = tensor_symbol.d + 1;
657
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);
658
35
  if (!tensor_info->s_ref)
659
15
  {
660
15
    tensor_info->s_ref = ccv_array_new(sizeof(int), graph->sub_graphs->rnum, 0);
661
15
    tensor_info->s_ref->rnum = graph->sub_graphs->rnum;
662
15
    ccv_array_zero(tensor_info->s_ref);
663
20
  } else if (tensor_info->s_ref->rnum != graph->sub_graphs->rnum)
664
20
    ccv_array_resize(tensor_info->s_ref, graph->sub_graphs->rnum);
665
35
  const int p_idx = sub_graph->p_idx - 1;
666
35
  assert(p_idx >= 0 && p_idx < tensor_info->s_ref->rnum);
667
35
  const int s_idx = *(int*)ccv_array_get(tensor_info->s_ref, p_idx);
668
35
  assert(s_idx == 0); // Otherwise it is assigned before
669
35
  *(int*)ccv_array_get(tensor_info->s_ref, p_idx) = sub_tensor_symbol.d + 1;
670
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);
671
35
  switch (map_use)
672
35
  {
673
7
    case MAP_TENSOR_USE_AS_INPUT:
674
7
      _ccv_nnc_graph_exec_add_input_if_needed(exec_symbol_info, tensor_symbol.d);
675
7
      break;
676
28
    case MAP_TENSOR_USE_AS_OUTPUT:
677
28
      _ccv_nnc_graph_exec_add_output_if_needed(exec_symbol_info, tensor_symbol.d);
678
28
      break;
679
35
  }
680
35
}
681
682
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)
683
11
{
684
11
  int i;
685
22
  for (i = 0; i < symbol_map_size; 
i++11
)
686
11
  {
687
11
    const ccv_nnc_tensor_symbol_t source = ccv_nnc_tensor_symbol_resolve(graph, symbol_map[i].source);
688
11
    const ccv_nnc_tensor_symbol_t destination = ccv_nnc_tensor_symbol_resolve(graph, symbol_map[i].destination);
689
11
    assert(source.graph == graph);
690
11
    assert(destination.graph == graph);
691
11
    assert(source.d < graph->tensor_symbol_info->rnum);
692
11
    assert(destination.d < graph->tensor_symbol_info->rnum);
693
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);
694
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);
695
    // Don't support parameterize with alias. The reason is that to support parameterized loop (for SSA), I choose
696
    // to simply reuse the piece of memory (allocating the same memory region to both, therefore to enable parameter
697
    // passing). For alias, it is not possible because alias can pointing to the tensors with different sizes, thus,
698
    // these pointed tensors cannot share the same memory region. The best way for alias to be parameterized is to
699
    // create a new tensor of the same size, transfer value over, and parameterized on that tensor instead.
700
11
    assert(!destination_tensor_symbol_info->alias_ref);
701
11
    assert(!source_tensor_symbol_info->alias_ref);
702
11
    destination_tensor_symbol_info->bypass_ref = source.d + 1;
703
11
    source_tensor_symbol_info->r_bypass_ref = destination.d + 1;
704
11
  }
705
11
}
706
707
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)
708
38.1k
{
709
38.1k
  assert(graph == tensor.graph);
710
38.1k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
711
38.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);
712
38.1k
  symbol_info->info = info;
713
  // It also need to propagate to assign_ref if needed.
714
38.1k
  if (symbol_info->assign_ref)
715
0
  {
716
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);
717
0
    assign_info->info = info;
718
0
  }
719
38.1k
  return 0;
720
38.1k
}
721
722
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)
723
66.8k
{
724
66.8k
  assert(graph == tensor.graph);
725
66.8k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
726
66.8k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
727
66.8k
  return symbol_info->info;
728
66.8k
}
729
730
int ccv_nnc_tensor_symbol_alias_set(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const int ofs[CCV_NNC_MAX_DIM_ALLOC], const int inc[CCV_NNC_MAX_DIM_ALLOC])
731
1.36k
{
732
1.36k
  assert(graph == tensor.graph);
733
1.36k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
734
1.36k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
735
1.36k
  if (!symbol_info->alias_ref)
736
0
    return -1;
737
1.36k
  memcpy(symbol_info->ofs, ofs, sizeof(symbol_info->ofs));
738
1.36k
  memcpy(symbol_info->inc, inc, sizeof(symbol_info->inc));
739
  // We don't need to propagate to assign_ref because alias cannot be loop carry-overs.
740
1.36k
  assert(!symbol_info->assign_ref);
741
1.36k
  return 0;
742
1.36k
}
743
744
int ccv_nnc_tensor_symbol_alias_params(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, int ofs[CCV_NNC_MAX_DIM_ALLOC], int inc[CCV_NNC_MAX_DIM_ALLOC])
745
39.1k
{
746
39.1k
  assert(graph == tensor.graph);
747
39.1k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
748
39.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);
749
39.1k
  if (!symbol_info->alias_ref)
750
36.8k
    return -1;
751
2.36k
  memcpy(ofs, symbol_info->ofs, sizeof(symbol_info->ofs));
752
2.36k
  memcpy(inc, symbol_info->inc, sizeof(symbol_info->inc));
753
2.36k
  return 0;
754
39.1k
}
755
756
int ccv_nnc_tensor_symbol_set_flags(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor, const int flags)
757
45.4k
{
758
45.4k
  assert(graph == tensor.graph);
759
45.4k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
760
45.4k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
761
45.4k
  symbol_info->flags = flags;
762
  // It also need to propagate to assign_ref if needed.
763
45.4k
  if (symbol_info->assign_ref)
764
1
  {
765
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);
766
1
    assign_info->flags = flags;
767
1
  }
768
45.4k
  return 0;
769
45.4k
}
770
771
int ccv_nnc_tensor_symbol_flags(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t tensor)
772
11
{
773
11
  assert(graph == tensor.graph);
774
11
  assert(tensor.d < graph->tensor_symbol_info->rnum);
775
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);
776
11
  return symbol_info->flags;
777
11
}
778
779
void ccv_nnc_tensor_symbol_free(ccv_nnc_symbolic_graph_t* const graph, ccv_nnc_tensor_symbol_t tensor)
780
71.4k
{
781
71.4k
  assert(graph == tensor.graph);
782
71.4k
  assert(tensor.d < graph->tensor_symbol_info->rnum);
783
71.4k
  ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, tensor.d);
784
71.4k
  if (symbol_info->s_ref)
785
0
  {
786
0
    ccv_array_free(symbol_info->s_ref);
787
0
    symbol_info->s_ref = 0;
788
0
  }
789
71.4k
  if (symbol_info->name)
790
8
  {
791
8
    ccfree(symbol_info->name);
792
8
    symbol_info->name = 0;
793
8
  }
794
71.4k
  symbol_info->flags |= CCV_NNC_TENSOR_SYMBOL_DEAD;
795
71.4k
  int i;
796
140k
  for (i = graph->tensor_symbol_info->rnum - 1; i >= 0; 
i--68.7k
)
797
137k
    if (!CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
798
68.8k
    {
799
68.8k
      graph->tensor_symbol_info->rnum = i + 1;
800
68.8k
      break;
801
68.8k
    }
802
71.4k
  if (tensor.d < graph->tensor_symbol_info->rnum &&
803
71.4k
    
(57.3k
tensor.d < graph->reuse.tensor57.3k
||
graph->reuse.tensor < 057.3k
))
804
13.2k
    graph->reuse.tensor = tensor.d;
805
58.1k
  else if (graph->reuse.tensor >= graph->tensor_symbol_info->rnum)
806
8.43k
    graph->reuse.tensor = -1;
807
71.4k
}
808
809
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)
810
55.3k
{
811
55.3k
  exec_info->input_size = input_size;
812
55.3k
  exec_info->output_size = output_size;
813
55.3k
  if (input_size > 0 || 
output_size > 04.39k
)
814
55.2k
  {
815
55.2k
    if (!exec_info->inputs)
816
54.6k
      exec_info->inputs = ccmalloc(sizeof(int) * (input_size + output_size));
817
636
    else
818
636
      exec_info->inputs = ccrealloc(exec_info->inputs, sizeof(int) * (input_size + output_size));
819
55.2k
    exec_info->outputs = exec_info->inputs + input_size;
820
55.2k
  }
821
55.3k
  int i;
822
55.3k
  int tensor_memory = 0, tensor_formats = 0, tensor_datatypes = 0, tensor_auto = 0;
823
198k
  for (i = 0; i < input_size; 
i++143k
)
824
143k
  {
825
143k
    const int d = ccv_nnc_tensor_symbol_map_raw(graph, inputs[i]);
826
143k
    exec_info->inputs[i] = d;
827
143k
    if (d >= 0)
828
115k
    {
829
115k
      const ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
830
115k
      tensor_auto = tensor_auto || ccv_nnc_is_tensor_auto(tensor_info->info);
831
115k
      tensor_memory |= CCV_TENSOR_GET_MEMORY(tensor_info->info.type), tensor_formats |= tensor_info->info.format, tensor_datatypes |= tensor_info->info.datatype;
832
115k
    }
833
143k
  }
834
134k
  for (i = 0; i < output_size; 
i++79.2k
)
835
79.2k
  {
836
79.2k
    const int d = (outputs[i].graph != graph && 
outputs[i].d >= 07.31k
) ?
_ccv_nnc_symbolic_graph_map_tensor_symbol(graph, outputs[i], MAP_TENSOR_USE_AS_OUTPUT)5
:
outputs[i].d79.2k
;
837
79.2k
    exec_info->outputs[i] = d;
838
79.2k
    if (d >= 0)
839
71.9k
    {
840
71.9k
      const ccv_nnc_tensor_symbol_info_t* const tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, d);
841
71.9k
      tensor_auto = tensor_auto || 
ccv_nnc_is_tensor_auto(tensor_info->info)71.9k
;
842
71.9k
      tensor_memory |= CCV_TENSOR_GET_MEMORY(tensor_info->info.type), tensor_formats |= tensor_info->info.format, tensor_datatypes |= tensor_info->info.datatype;
843
71.9k
    }
844
79.2k
  }
845
  // If there is no auto tensor, we try to find backend (we don't know which backend if the tensor is auto).
846
55.3k
  if (!tensor_auto)
847
55.2k
    exec_info->cmd.backend = ccv_nnc_cmd_find_backend(exec_info->cmd, tensor_memory, tensor_formats, tensor_datatypes);
848
55.3k
}
849
850
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)
851
54.6k
{
852
54.6k
  ccv_nnc_graph_exec_symbol_t symbol = {
853
54.6k
    .d = graph->exec_symbol_info->rnum,
854
54.6k
    .graph = graph
855
54.6k
  };
856
54.6k
  ccv_nnc_graph_exec_symbol_info_t symbol_info = {
857
54.6k
    .cmd = cmd,
858
54.6k
    .hint = ccv_nnc_no_hint,
859
54.6k
  };
860
54.6k
  if (name)
861
482
  {
862
482
    const size_t len = strnlen(name, 63);
863
482
    const size_t n = len + 1;
864
482
    symbol_info.name = (char*)ccmalloc(n);
865
    // Don't use strndup because this way I can have custom allocator (for ccmalloc).
866
482
    memcpy(symbol_info.name, name, n);
867
482
    symbol_info.name[len] = 0;
868
482
  }
869
54.6k
  _ccv_nnc_graph_exec_symbol_set_io(graph, &symbol_info, inputs, input_size, outputs, output_size);
870
54.6k
  if (graph->reuse.exec >= 0)
871
9.61k
  {
872
9.61k
    const int reuse_exec_d = graph->reuse.exec;
873
9.61k
    assert(reuse_exec_d < graph->exec_symbol_info->rnum);
874
9.61k
    *(ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, reuse_exec_d) = symbol_info;
875
9.61k
    int i;
876
9.61k
    graph->reuse.exec = -1;
877
14.4k
    for (i = reuse_exec_d + 1; i < graph->exec_symbol_info->rnum && 
graph->reuse.exec < 07.22k
;
i++4.81k
)
878
4.81k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i))->flags))
879
4.80k
        graph->reuse.exec = i;
880
9.61k
    symbol.d = reuse_exec_d;
881
9.61k
  } else
882
45.0k
    ccv_array_push(graph->exec_symbol_info, &symbol_info);
883
54.6k
  if (graph->hooks.graph_exec_symbol_new.func)
884
33.6k
    graph->hooks.graph_exec_symbol_new.func(graph->hooks.graph_exec_symbol_new.context, symbol, cmd, inputs, input_size, outputs, output_size, name);
885
54.6k
  return symbol;
886
54.6k
}
887
888
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)
889
16.1k
{
890
16.1k
  void* const prev = graph->hooks.graph_exec_symbol_new.context;
891
16.1k
  graph->hooks.graph_exec_symbol_new.func = hook;
892
16.1k
  graph->hooks.graph_exec_symbol_new.context = context;
893
16.1k
  return prev;
894
16.1k
}
895
896
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)
897
636
{
898
636
  assert(exec.graph == graph);
899
636
  assert(exec.d >= 0);
900
636
  assert(exec.d < graph->exec_symbol_info->rnum);
901
636
  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);
902
636
  _ccv_nnc_graph_exec_symbol_set_io(graph, exec_info, inputs, input_size, outputs, output_size);
903
636
}
904
905
void ccv_nnc_graph_exec_symbol_pair_with(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t exec_symbol, const ccv_nnc_graph_exec_symbol_t pair_exec_symbol)
906
18.0k
{
907
18.0k
  assert(exec_symbol.graph == graph);
908
18.0k
  assert(exec_symbol.d >= 0);
909
18.0k
  assert(exec_symbol.d < graph->exec_symbol_info->rnum);
910
18.0k
  assert(pair_exec_symbol.graph == graph || pair_exec_symbol.graph == graph->pair);
911
18.0k
  assert(pair_exec_symbol.d >= 0);
912
18.0k
  if (pair_exec_symbol.graph == graph)
913
18.0k
    { assert(pair_exec_symbol.d < graph->exec_symbol_info->rnum); }
914
4
  else
915
4
    { assert(pair_exec_symbol.d < graph->pair->exec_symbol_info->rnum); }
916
18.0k
  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);
917
18.0k
  exec_info->pair_ref = pair_exec_symbol.d + 1;
918
18.0k
}
919
920
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)
921
42.4k
{
922
42.4k
  assert(graph == exec.graph);
923
42.4k
  assert(exec.d < graph->exec_symbol_info->rnum);
924
42.4k
  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);
925
42.4k
  symbol_info->cmd = cmd;
926
42.4k
}
927
928
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)
929
48.8k
{
930
48.8k
  assert(graph == exec.graph);
931
48.8k
  assert(exec.d < graph->exec_symbol_info->rnum);
932
48.8k
  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);
933
48.8k
  return symbol_info->cmd;
934
48.8k
}
935
936
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)
937
20.1k
{
938
20.1k
  assert(graph == exec.graph);
939
20.1k
  assert(exec.d < graph->exec_symbol_info->rnum);
940
20.1k
  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);
941
20.1k
  symbol_info->hint = hint;
942
20.1k
}
943
944
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)
945
68.9k
{
946
68.9k
  assert(graph == source.graph);
947
68.9k
  assert(graph == destination.graph);
948
68.9k
  assert(source.d < graph->exec_symbol_info->rnum);
949
68.9k
  assert(destination.d < graph->exec_symbol_info->rnum);
950
68.9k
  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);
951
68.9k
  if (!src_symbol_info->outgoings)
952
45.3k
    src_symbol_info->outgoings = ccv_array_new(sizeof(int32_t), 1, 0);
953
23.6k
  else {
954
23.6k
    int i;
955
    // Check if this is already connected, if so, skip.
956
42.5k
    for (i = 0; i < src_symbol_info->outgoings->rnum; 
i++18.9k
)
957
27.6k
      if (*(int*)ccv_array_get(src_symbol_info->outgoings, i) == destination.d)
958
8.66k
        return -1;
959
23.6k
  }
960
60.2k
  ccv_array_push(src_symbol_info->outgoings, &destination.d);
961
60.2k
  return 0;
962
68.9k
}
963
964
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)
965
84.2k
{
966
84.2k
  assert(graph == symbol.graph);
967
84.2k
  assert(symbol.d < graph->exec_symbol_info->rnum);
968
84.2k
  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);
969
84.2k
  if (inputs)
970
58.8k
    *inputs = symbol_info->inputs;
971
84.2k
  if (input_size)
972
66.7k
    *input_size = symbol_info->input_size;
973
84.2k
  if (outputs)
974
71.6k
    *outputs = symbol_info->outputs;
975
84.2k
  if (output_size)
976
79.5k
    *output_size = symbol_info->output_size;
977
84.2k
}
978
979
void ccv_nnc_graph_exec_symbol_replace_io(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t symbol, const ccv_nnc_tensor_symbol_t old_symbol, const ccv_nnc_tensor_symbol_t new_symbol)
980
4
{
981
4
  assert(graph == symbol.graph);
982
4
  assert(symbol.d < graph->exec_symbol_info->rnum);
983
4
  assert(graph == old_symbol.graph);
984
4
  assert(old_symbol.d < graph->tensor_symbol_info->rnum);
985
4
  assert(graph == new_symbol.graph);
986
4
  assert(new_symbol.d < graph->tensor_symbol_info->rnum);
987
4
  const ccv_nnc_tensor_symbol_info_t* const old_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, old_symbol.d);
988
4
  const ccv_nnc_tensor_symbol_info_t* const new_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, new_symbol.d);
989
4
  if (old_tensor_info != new_tensor_info)
990
4
  {
991
    // These need to be the same, otherwise we need to find the backend again for this exec. See _ccv_nnc_graph_exec_symbol_set_io
992
4
    assert(ccv_nnc_is_tensor_auto(old_tensor_info->info) == ccv_nnc_is_tensor_auto(new_tensor_info->info));
993
4
    assert(old_tensor_info->info.type == new_tensor_info->info.type);
994
4
    assert(old_tensor_info->info.format == new_tensor_info->info.format);
995
4
    assert(old_tensor_info->info.datatype == new_tensor_info->info.datatype);
996
4
  }
997
4
  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);
998
4
  int i;
999
12
  for (i = 0; i < symbol_info->input_size; 
i++8
)
1000
8
    if (symbol_info->inputs[i] == old_symbol.d)
1001
4
      symbol_info->inputs[i] = new_symbol.d;
1002
8
  for (i = 0; i < symbol_info->output_size; 
i++4
)
1003
4
    if (symbol_info->outputs[i] == old_symbol.d)
1004
0
      symbol_info->outputs[i] = new_symbol.d;
1005
4
}
1006
1007
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)
1008
37.5k
{
1009
37.5k
  assert(graph == symbol.graph);
1010
37.5k
  assert(symbol.d < graph->exec_symbol_info->rnum);
1011
37.5k
  assert(tos);
1012
37.5k
  assert(to_size);
1013
37.5k
  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);
1014
37.5k
  if (!symbol_info->outgoings)
1015
4.49k
  {
1016
4.49k
    *tos = 0;
1017
4.49k
    *to_size = 0;
1018
4.49k
    return;
1019
4.49k
  }
1020
33.0k
  *to_size = symbol_info->outgoings->rnum;
1021
33.0k
  *tos = (int*)ccv_array_get(symbol_info->outgoings, 0);
1022
33.0k
}
1023
1024
int ccv_nnc_graph_exec_symbol_count(const ccv_nnc_symbolic_graph_t* const graph)
1025
19.2k
{
1026
19.2k
  return graph->exec_symbol_info->rnum;
1027
19.2k
}
1028
1029
int ccv_nnc_symbolic_graph_active_symbol_count(const ccv_nnc_symbolic_graph_t* const graph, const int type)
1030
451
{
1031
451
  assert(type == CCV_NNC_SYMBOL_TENSOR || type == CCV_NNC_SYMBOL_GRAPH_EXEC);
1032
451
  if (type == CCV_NNC_SYMBOL_GRAPH_EXEC)
1033
422
  {
1034
422
    int i, count = graph->exec_symbol_info->rnum;
1035
864
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++442
)
1036
442
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i))->flags))
1037
416
        --count;
1038
422
    return count;
1039
422
  } else 
if (29
type == CCV_NNC_SYMBOL_TENSOR29
) {
1040
29
    int i, count = graph->tensor_symbol_info->rnum;
1041
134
    for (i = 0; i < graph->tensor_symbol_info->rnum; 
i++105
)
1042
105
      if (CCV_NNC_TENSOR_SYMBOL_IS_DEAD(((ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i))->flags))
1043
54
        --count;
1044
29
    return count;
1045
29
  }
1046
0
  return 0;
1047
451
}
1048
1049
int ccv_nnc_tensor_symbol_count(const ccv_nnc_symbolic_graph_t* const graph)
1050
52
{
1051
52
  return graph->tensor_symbol_info->rnum;
1052
52
}
1053
1054
static inline void _ccv_nnc_graph_exec_symbol_free(ccv_nnc_graph_exec_symbol_info_t* const symbol_info, const int zeroing)
1055
54.8k
{
1056
54.8k
  if (symbol_info->name)
1057
510
    ccfree(symbol_info->name);
1058
54.8k
  if (symbol_info->_heap_graph_ref)
1059
7
    ccfree(symbol_info->_heap_graph_ref);
1060
54.8k
  ccv_array_t* outgoings = symbol_info->outgoings;
1061
54.8k
  if (outgoings)
1062
45.3k
    ccv_array_free(outgoings);
1063
  // We allocate inputs & outputs in continuous fashion, therefore, only need to free the input array.
1064
54.8k
  if (symbol_info->inputs)
1065
54.6k
    ccfree(symbol_info->inputs);
1066
54.8k
  if (symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE)
1067
24
    if (symbol_info->p_while.inputs)
1068
19
      ccfree(symbol_info->p_while.inputs);
1069
54.8k
  if (zeroing)
1070
43.5k
  {
1071
43.5k
    symbol_info->name = 0;
1072
43.5k
    symbol_info->_heap_graph_ref = 0;
1073
43.5k
    symbol_info->outgoings = 0;
1074
43.5k
    symbol_info->inputs = 0;
1075
43.5k
    symbol_info->input_size = 0;
1076
43.5k
    symbol_info->outputs = 0;
1077
43.5k
    symbol_info->output_size = 0;
1078
43.5k
  }
1079
54.8k
}
1080
1081
void ccv_nnc_graph_exec_symbol_free(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t symbol)
1082
43.5k
{
1083
43.5k
  assert(graph == symbol.graph);
1084
43.5k
  assert(symbol.d < graph->exec_symbol_info->rnum);
1085
  // If any of the exec symbol have reference to it, has to remove that.
1086
43.5k
  int i, j, k;
1087
43.5k
  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);
1088
333k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++290k
)
1089
290k
    if (i != symbol.d)
1090
246k
    {
1091
246k
      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);
1092
246k
      if (symbol_info->outgoings)
1093
349k
        
for (j = 0; 171k
j < symbol_info->outgoings->rnum;
j++177k
)
1094
227k
          if (*(int*)ccv_array_get(symbol_info->outgoings, j) == symbol.d)
1095
49.4k
          {
1096
49.4k
            if (j < symbol_info->outgoings->rnum - 1)
1097
62
              *(int*)ccv_array_get(symbol_info->outgoings, j) = *(int*)ccv_array_get(symbol_info->outgoings, symbol_info->outgoings->rnum - 1);
1098
49.4k
            --symbol_info->outgoings->rnum;
1099
49.4k
            if (free_symbol_info->outgoings)
1100
61.8k
              
for (k = 0; 35.5k
k < free_symbol_info->outgoings->rnum;
k++26.3k
)
1101
26.3k
                ccv_array_add_unique_int(symbol_info->outgoings, *(int*)ccv_array_get(free_symbol_info->outgoings, k));
1102
49.4k
            break;
1103
49.4k
          }
1104
246k
    }
1105
  // Deallocate any memory for exec symbol.
1106
43.5k
  _ccv_nnc_graph_exec_symbol_free(free_symbol_info, 1);
1107
43.5k
  free_symbol_info->flags = CCV_NNC_GRAPH_EXEC_DEAD; // Mark this as dead.
1108
  // If everything from symbol.d to the end of the graph is dead, we can reclaim this memory.
1109
87.0k
  for (i = graph->exec_symbol_info->rnum - 1; i >= 0; 
i--43.4k
)
1110
82.1k
    if (!CCV_NNC_GRAPH_EXEC_IS_DEAD(((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i))->flags))
1111
38.6k
    {
1112
38.6k
      graph->exec_symbol_info->rnum = i + 1;
1113
38.6k
      break;
1114
38.6k
    }
1115
  // Loop over sources and destinations to remove this.
1116
43.5k
  if (graph->sources)
1117
82
    
for (i = 0; 33
i < graph->sources->rnum;
i++49
)
1118
57
      if (*(int*)ccv_array_get(graph->sources, i) == symbol.d)
1119
8
      {
1120
8
        if (i < graph->sources->rnum - 1)
1121
1
          *(int*)ccv_array_get(graph->sources, i) = *(int*)ccv_array_get(graph->sources, graph->sources->rnum - 1);
1122
8
        --graph->sources->rnum;
1123
8
        break;
1124
8
      }
1125
43.5k
  if (graph->destinations)
1126
67
    
for (i = 0; 33
i < graph->destinations->rnum;
i++34
)
1127
45
      if (*(int*)ccv_array_get(graph->destinations, i) == symbol.d)
1128
11
      {
1129
11
        if (i < graph->destinations->rnum - 1)
1130
4
          *(int*)ccv_array_get(graph->destinations, i) = *(int*)ccv_array_get(graph->destinations, graph->destinations->rnum - 1);
1131
11
        --graph->destinations->rnum;
1132
11
        break;
1133
11
      }
1134
43.5k
  if (symbol.d < graph->exec_symbol_info->rnum &&
1135
43.5k
    
(27.8k
symbol.d < graph->reuse.exec27.8k
||
graph->reuse.exec < 027.8k
))
1136
9.27k
    graph->reuse.exec = symbol.d;
1137
34.2k
  else if (graph->reuse.exec >= graph->exec_symbol_info->rnum)
1138
4.43k
    graph->reuse.exec = -1;
1139
43.5k
}
1140
1141
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)
1142
10
{
1143
10
  assert(graph == source.graph);
1144
10
  assert(graph == destination.graph);
1145
10
  assert(source.d < graph->exec_symbol_info->rnum);
1146
10
  assert(destination.d < graph->exec_symbol_info->rnum);
1147
10
  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);
1148
10
  if (!src_symbol_info->outgoings)
1149
0
    return -1;
1150
10
  int i;
1151
  // Check if this is already disjoined, if so, skip.
1152
10
  for (i = 0; i < src_symbol_info->outgoings->rnum; 
i++0
)
1153
10
    if (*(int*)ccv_array_get(src_symbol_info->outgoings, i) == destination.d)
1154
10
    {
1155
10
      if (i < src_symbol_info->outgoings->rnum - 1)
1156
1
        *(int*)ccv_array_get(src_symbol_info->outgoings, i) = *(int*)ccv_array_get(src_symbol_info->outgoings, src_symbol_info->outgoings->rnum - 1);
1157
10
      --src_symbol_info->outgoings->rnum;
1158
10
      return 0;
1159
10
    }
1160
0
  return -1;
1161
10
}
1162
1163
433k
#define CCV_NNC_IS_AUTOGEN_ALL_EXECS(x) ((x) & CCV_NNC_AUTOGEN_ALL_EXECS)
1164
11.5k
#define CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(x) ((x) & CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS)
1165
1166
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)
1167
89
{
1168
89
  int i;
1169
89
  const int* inc = tensor_a->inc;
1170
  // Only can compare if the inc is the same, otherwise, we can only assume it overlaps.
1171
89
  if (memcmp(inc, tensor_b->inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC) != 0)
1172
0
    return 1;
1173
89
  const int* ofs = tensor_a->ofs;
1174
89
  const int* dim = tensor_a->info.dim;
1175
219
  for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && dim[i] && 
tensor_b->info.dim[i]162
;
i++130
)
1176
162
    if (ccv_min(ofs[i] + dim[i], tensor_b->ofs[i] + tensor_b->info.dim[i]) <= ccv_max(ofs[i], tensor_b->ofs[i]))
1177
32
      return 0; // Cannot overlap.
1178
57
  return 1;
1179
89
}
1180
1181
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)
1182
11.5k
{
1183
11.5k
  int i, j, x, y;
1184
11.6k
  for (i = 0; i < exec_size; 
i++43
)
1185
43
    if (execs[i].graph == graph)
1186
43
    {
1187
43
      assert(execs[i].d >= 0);
1188
43
      assert(execs[i].d < graph->exec_symbol_info->rnum);
1189
43
    }
1190
11.5k
  if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
exec_size4.57k
)
1191
11
    { assert(execs); }
1192
11.5k
  const int exec_total_size = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
graph->exec_symbol_info->rnum6.99k
:
exec_size4.57k
;
1193
25.9k
  for (i = 0; i < exec_total_size; 
i++14.3k
)
1194
14.3k
  {
1195
14.3k
    if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[i].graph != graph43
)
1196
0
      continue;
1197
14.3k
    int idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
i14.2k
:
execs[i].d43
;
1198
14.3k
    ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, idx);
1199
14.3k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
1200
2
      continue;
1201
    // Autogen for sub-graphs.
1202
14.3k
    if (CCV_NNC_GRAPH_REF(symbol_info)[0])
1203
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);
1204
14.3k
  }
1205
25.9k
  for (i = 0; i < exec_total_size; 
i++14.3k
)
1206
14.3k
  {
1207
14.3k
    if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[i].graph != graph43
)
1208
0
      continue;
1209
14.3k
    int a_idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
i14.2k
:
execs[i].d43
;
1210
14.3k
    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);
1211
14.3k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(a_symbol_info->flags))
1212
2
      continue;
1213
118k
    
for (j = i + 1; 14.3k
j < exec_total_size;
j++104k
)
1214
104k
    {
1215
104k
      if (!CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) && 
execs[j].graph != graph69
)
1216
0
        continue;
1217
104k
      int b_idx = CCV_NNC_IS_AUTOGEN_ALL_EXECS(flags) ? 
j104k
:
execs[j].d69
;
1218
      // Skip if they are the same.
1219
104k
      if (a_idx == b_idx)
1220
0
        continue;
1221
104k
      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);
1222
104k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(b_symbol_info->flags))
1223
4
        continue;
1224
104k
      int b_to_a = 0;
1225
397k
      for (x = 0; x < a_symbol_info->input_size && 
!b_to_a293k
;
x++293k
)
1226
293k
      {
1227
293k
        int a = a_symbol_info->inputs[x];
1228
293k
        if (a < 0)
1229
40.2k
          continue;
1230
        // Handle alias as well.
1231
252k
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
1232
252k
        if (a_tensor_info->alias_ref)
1233
20.8k
          a = a_tensor_info->alias_ref - 1;
1234
724k
        for (y = 0; y < b_symbol_info->output_size && 
!b_to_a471k
;
y++471k
)
1235
471k
        {
1236
471k
          int b = b_symbol_info->outputs[y];
1237
471k
          if (b < 0)
1238
7.75k
            continue;
1239
463k
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
1240
463k
          if (b_tensor_info->alias_ref)
1241
14.8k
            b = b_tensor_info->alias_ref - 1;
1242
463k
          if (a == b && // This two have matching inputs and outputs.
1243
463k
            
(415
!a_tensor_info->alias_ref415
||
1244
415
             
!b_tensor_info->alias_ref6
|| // If any of them are not alias, the must overlap, you can concatenate.
1245
415
             
ccv_nnc_over_tensor_symbol_aliases(a_tensor_info, b_tensor_info)6
)) // Otherwise, we explicitly check whether it overlaps, if it does, concatenate.
1246
409
            b_to_a = 1;
1247
463k
        }
1248
252k
      }
1249
104k
      if (b_to_a)
1250
409
      {
1251
409
        if (execs)
1252
0
          ccv_nnc_graph_exec_symbol_concat(graph, execs[j], execs[i]);
1253
409
        else
1254
409
          ccv_nnc_graph_exec_symbol_concat(graph,
1255
409
            (ccv_nnc_graph_exec_symbol_t) {
1256
409
              .d = j,
1257
409
              .graph = graph
1258
409
            }, (ccv_nnc_graph_exec_symbol_t) {
1259
409
              .d = i,
1260
409
              .graph = graph
1261
409
            }
1262
409
          );
1263
409
      }
1264
104k
      int a_to_b = 0;
1265
290k
      for (x = 0; x < a_symbol_info->output_size && 
!a_to_b187k
;
x++186k
)
1266
186k
      {
1267
186k
        int a = a_symbol_info->outputs[x];
1268
186k
        if (a < 0)
1269
3.34k
          continue;
1270
        // Handle alias as well.
1271
182k
        ccv_nnc_tensor_symbol_info_t* a_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, a);
1272
182k
        if (a_tensor_info->alias_ref)
1273
6.28k
          a = a_tensor_info->alias_ref - 1;
1274
731k
        for (y = 0; y < b_symbol_info->input_size && 
!a_to_b552k
;
y++548k
)
1275
548k
        {
1276
548k
          int b = b_symbol_info->inputs[y];
1277
548k
          if (b < 0)
1278
73.2k
            continue;
1279
475k
          ccv_nnc_tensor_symbol_info_t* b_tensor_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, b);
1280
475k
          if (b_tensor_info->alias_ref)
1281
15.1k
            b = b_tensor_info->alias_ref - 1;
1282
475k
          if (a == b && // This two have matching inputs and outputs.
1283
475k
            
(5.19k
!a_tensor_info->alias_ref5.19k
||
1284
5.19k
             
!b_tensor_info->alias_ref53
|| // If any of them are not alias, the must overlap, you can concatenate.
1285
5.19k
             
ccv_nnc_over_tensor_symbol_aliases(a_tensor_info, b_tensor_info)19
)) // Otherwise, we explicitly check whether it overlaps, if it does, concatenate.
1286
5.19k
            a_to_b = 1;
1287
475k
        }
1288
182k
      }
1289
104k
      if (a_to_b)
1290
5.19k
      {
1291
5.19k
        if (execs)
1292
41
          ccv_nnc_graph_exec_symbol_concat(graph, execs[i], execs[j]);
1293
5.15k
        else
1294
5.15k
          ccv_nnc_graph_exec_symbol_concat(graph,
1295
5.15k
            (ccv_nnc_graph_exec_symbol_t) {
1296
5.15k
              .d = i,
1297
5.15k
              .graph = graph
1298
5.15k
            }, (ccv_nnc_graph_exec_symbol_t) {
1299
5.15k
              .d = j,
1300
5.15k
              .graph = graph
1301
5.15k
            }
1302
5.15k
          );
1303
5.19k
      }
1304
104k
    }
1305
14.3k
  }
1306
  // If flag says so, loop over to find sources / destinations too.
1307
11.5k
  if (CCV_NNC_IS_AUTOGEN_SOURCES_AND_DESTINATIONS(flags))
1308
9.32k
  {
1309
9.32k
    uint8_t* flags = (uint8_t*)cccalloc(sizeof(uint8_t), graph->exec_symbol_info->rnum);
1310
24.4k
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++15.0k
)
1311
15.0k
    {
1312
15.0k
      ccv_nnc_graph_exec_symbol_info_t* symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i);
1313
15.0k
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(symbol_info->flags))
1314
12
      {
1315
12
        flags[i] = 3; // Skip.
1316
12
        continue;
1317
12
      }
1318
15.0k
      if (symbol_info->outgoings && 
symbol_info->outgoings->rnum5.04k
)
1319
5.03k
      {
1320
5.03k
        flags[i] |= 2;
1321
13.1k
        for (j = 0; j < symbol_info->outgoings->rnum; 
j++8.08k
)
1322
8.08k
          flags[*(int*)ccv_array_get(symbol_info->outgoings, j)] |= 1;
1323
5.03k
      }
1324
15.0k
    }
1325
9.32k
    if (!graph->sources)
1326
2.43k
      graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1327
6.88k
    else
1328
6.88k
      ccv_array_clear(graph->sources);
1329
9.32k
    if (!graph->destinations)
1330
2.43k
      graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1331
6.88k
    else
1332
6.88k
      ccv_array_clear(graph->destinations);
1333
24.4k
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++15.0k
)
1334
15.0k
    {
1335
15.0k
      if (flags[i] == 3)
1336
4.49k
        continue;
1337
10.5k
      ccv_nnc_graph_exec_symbol_t exec = {
1338
10.5k
        .d = i,
1339
10.5k
        .graph = graph,
1340
10.5k
      };
1341
10.5k
      if (!(flags[i] & 1))
1342
9.53k
        ccv_array_push(graph->sources, &exec);
1343
10.5k
      if (!(flags[i] & 2))
1344
10.0k
        ccv_array_push(graph->destinations, &exec);
1345
10.5k
    }
1346
9.32k
    ccfree(flags);
1347
9.32k
  }
1348
11.5k
  return 0;
1349
11.5k
}
1350
1351
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_sources(const ccv_nnc_symbolic_graph_t* const graph)
1352
7.09k
{
1353
7.09k
  return graph->sources ? (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->sources, 0) : 
00
;
1354
7.09k
}
1355
1356
void ccv_nnc_symbolic_graph_add_source(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t source)
1357
17
{
1358
17
  if (!graph->sources)
1359
0
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1360
17
  assert(source.graph == graph);
1361
17
  ccv_array_push(graph->sources, &source);
1362
17
}
1363
1364
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)
1365
15
{
1366
15
  if (!graph->sources)
1367
11
    graph->sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1368
4
  else
1369
4
    ccv_array_clear(graph->sources);
1370
15
  int i;
1371
30
  for (i = 0; i < source_size; 
i++15
)
1372
15
    ccv_nnc_symbolic_graph_add_source(graph, sources[i]);
1373
15
}
1374
1375
int ccv_nnc_symbolic_graph_source_size(const ccv_nnc_symbolic_graph_t* const graph)
1376
7.09k
{
1377
7.09k
  return graph->sources ? graph->sources->rnum : 
00
;
1378
7.09k
}
1379
1380
ccv_nnc_graph_exec_symbol_t* ccv_nnc_symbolic_graph_destinations(const ccv_nnc_symbolic_graph_t* const graph)
1381
9.32k
{
1382
9.32k
  return graph->destinations ? (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->destinations, 0) : 
00
;
1383
9.32k
}
1384
1385
void ccv_nnc_symbolic_graph_add_destination(ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_exec_symbol_t destination)
1386
2.71k
{
1387
2.71k
  if (!graph->destinations)
1388
0
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1389
2.71k
  assert(destination.graph == graph);
1390
2.71k
  ccv_array_push(graph->destinations, &destination);
1391
2.71k
}
1392
1393
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)
1394
2.25k
{
1395
2.25k
  if (!graph->destinations)
1396
11
    graph->destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 0, 0);
1397
2.24k
  else
1398
2.24k
    ccv_array_clear(graph->destinations);
1399
2.25k
  int i;
1400
4.94k
  for (i = 0; i < destination_size; 
i++2.69k
)
1401
2.69k
    ccv_nnc_symbolic_graph_add_destination(graph, destinations[i]);
1402
2.25k
}
1403
1404
int ccv_nnc_symbolic_graph_destination_size(const ccv_nnc_symbolic_graph_t* const graph)
1405
9.31k
{
1406
9.31k
  return graph->destinations ? graph->destinations->rnum : 
00
;
1407
9.31k
}
1408
1409
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)
1410
862
{
1411
862
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1412
833
    fputc('{', out);
1413
862
  if (symbol_info->name)
1414
379
    fputs(symbol_info->name, out);
1415
483
  else
1416
483
    fprintf(out, "node%d", index);
1417
862
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1418
833
  {
1419
833
    fputs("|Command: ", out);
1420
833
    fputs(ccv_nnc_cmd_name(symbol_info->cmd.cmd), out);
1421
833
    fputc('}', out);
1422
833
  }
1423
862
}
1424
1425
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)
1426
2.75k
{
1427
  // if it has an alias pointer, or, it is a long form.
1428
2.75k
  if ((flags == CCV_NNC_LONG_DOT_GRAPH || 
alias_info79
) &&
!html_like2.70k
)
1429
2.61k
    fputc('{', out);
1430
2.75k
  if (symbol_info->name)
1431
1.21k
    fputs(symbol_info->name, out);
1432
1.53k
  else
1433
1.53k
    fprintf(out, "tensor%d", index);
1434
2.75k
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1435
2.67k
  {
1436
2.67k
    int flag = -1;
1437
2.67k
    if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS)
1438
16
      flag = fputs(" (0", out); // Output if it is zero init'ed.
1439
2.65k
    else if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ONES)
1440
4
        flag = fputs(" (1", out); // Output if it is one init'ed.
1441
2.67k
    if (symbol_info->flags & CCV_NNC_TENSOR_SYMBOL_TAPE_VAR)
1442
16
      flag = (flag >= 0) ? 
fputs(",t", out)0
: fputs(" (t", out); // Output is a tape variable
1443
2.67k
    if (CCV_TENSOR_GET_MEMORY(symbol_info->info.type) == CCV_TENSOR_GPU_MEMORY &&
1444
2.67k
      
CCV_TENSOR_GET_DEVICE845
(symbol_info->info.type) != CCV_COMPUTE_DEVICE_ANY845
)
1445
845
      flag = (flag >= 0) ? 
fprintf(out, ",d%d", 8
CCV_TENSOR_GET_DEVICE_ID8
(symbol_info->info.type)) :
fprintf(out, " (d%d", 837
CCV_TENSOR_GET_DEVICE_ID837
(symbol_info->info.type));
1446
2.67k
    if (flag >= 0)
1447
873
      fputs(")", out);
1448
2.67k
  }
1449
2.75k
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1450
2.67k
  {
1451
2.67k
    int i;
1452
2.67k
    if (html_like)
1453
86
      fprintf(out, "</td><td>%d", symbol_info->info.dim[0]);
1454
2.58k
    else
1455
2.58k
      fprintf(out, "|%d", symbol_info->info.dim[0]);
1456
5.38k
    for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && symbol_info->info.dim[i]; 
i++2.71k
)
1457
2.71k
      fprintf(out, "x%d", symbol_info->info.dim[i]);
1458
2.67k
  }
1459
2.75k
  if (alias_info)
1460
175
  {
1461
175
    if (html_like)
1462
0
      fputs("</td><td border=\"0\">as. ", out);
1463
175
    else
1464
175
      fputs("|as. ", out);
1465
175
    if (alias_info->name)
1466
99
      fputs(alias_info->name, out);
1467
76
    else
1468
76
      fprintf(out, "tensor%d", symbol_info->alias_ref - 1);
1469
175
    if (flags == CCV_NNC_LONG_DOT_GRAPH)
1470
143
    {
1471
143
      int flag = -1;
1472
143
      if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS)
1473
7
        flag = fputs(" (0", out); // Output if it is zero init'ed.
1474
136
      else if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_INIT_ONES)
1475
0
        flag = fputs(" (1", out); // Output if it is one init'ed.
1476
143
      if (alias_info->flags & CCV_NNC_TENSOR_SYMBOL_TAPE_VAR)
1477
0
        flag = (flag >= 0) ? fputs(",t", out) : fputs(" (t", out); // Output is a tape variable
1478
143
      if (CCV_TENSOR_GET_MEMORY(alias_info->info.type) == CCV_TENSOR_GPU_MEMORY &&
1479
143
        
CCV_TENSOR_GET_DEVICE12
(alias_info->info.type) != CCV_COMPUTE_DEVICE_ANY12
)
1480
12
        flag = (flag >= 0) ? 
fprintf(out, ",d%d", 0
CCV_TENSOR_GET_DEVICE_ID0
(alias_info->info.type)) : fprintf(out, " (d%d", CCV_TENSOR_GET_DEVICE_ID(alias_info->info.type));
1481
143
      if (flag >= 0)
1482
19
        fputs(")", out);
1483
143
    }
1484
175
  }
1485
2.75k
  if ((flags == CCV_NNC_LONG_DOT_GRAPH || 
alias_info79
) &&
!html_like2.70k
)
1486
2.61k
    fputc('}', out);
1487
2.75k
}
1488
1489
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)
1490
862
{
1491
862
  fprintf(out, "node%d [shape=record,label=\"", index);
1492
862
  _ccv_nnc_symbolic_graph_dot_exec_symbol(index, exec_symbol_info, flags, out);
1493
862
  int i;
1494
862
  if (exec_symbol_info->input_size > 0)
1495
790
  {
1496
790
    fputs("|{Input", out);
1497
2.73k
    for (i = 0; i < exec_symbol_info->input_size; 
i++1.94k
)
1498
1.94k
    {
1499
1.94k
      if (exec_symbol_info->inputs[i] >= 0)
1500
1.61k
      {
1501
1.61k
        fputc('|', out);
1502
1.61k
        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]);
1503
1.61k
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)105
ccv_array_get105
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
01.50k
;
1504
1.61k
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 0, flags, out);
1505
1.61k
      } else
1506
335
        fputs("|-", out);
1507
1.94k
    }
1508
790
    fputc('}', out);
1509
790
  }
1510
862
  if (exec_symbol_info->output_size > 0)
1511
841
  {
1512
841
    fputs("|{Output", out);
1513
1.94k
    for (i = 0; i < exec_symbol_info->output_size; 
i++1.10k
)
1514
1.10k
    {
1515
1.10k
      if (exec_symbol_info->outputs[i] >= 0)
1516
1.05k
      {
1517
1.05k
        fputc('|', out);
1518
1.05k
        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]);
1519
1.05k
        const ccv_nnc_tensor_symbol_info_t* const alias_symbol = tensor_symbol->alias_ref ? 
(ccv_nnc_tensor_symbol_info_t*)70
ccv_array_get70
(tensor_symbol_info, tensor_symbol->alias_ref - 1) :
0982
;
1520
1.05k
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 0, flags, out);
1521
1.05k
      } else
1522
49
        fputs("|-", out);
1523
1.10k
    }
1524
841
    fputc('}', out);
1525
841
  }
1526
862
  fputs("\"];\n", out);
1527
862
}
1528
1529
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)
1530
21
{
1531
21
  int i;
1532
21
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1533
21
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"3\" border=\"0\"><b>", out);
1534
0
  else
1535
0
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"2\" border=\"0\"><b>", out);
1536
21
  if (exec_symbol_info->name)
1537
21
    fputs(exec_symbol_info->name, out);
1538
0
  else
1539
0
    fprintf(out, "while%d", index);
1540
21
  fputs(" </b>Command: ", out);
1541
21
  fputs(ccv_nnc_cmd_name(exec_symbol_info->cmd.cmd), out);
1542
21
  fputs("</td></tr>", out);
1543
21
  const int p_idx = while_graph->p_idx - 1;
1544
21
  assert(p_idx >= 0);
1545
21
  if (exec_symbol_info->input_size > 0)
1546
16
  {
1547
16
    fprintf(out, "<tr><td rowspan=\"%d\">Input</td>", exec_symbol_info->input_size);
1548
39
    for (i = 0; i < exec_symbol_info->input_size; 
i++23
)
1549
23
    {
1550
23
      if (i > 0)
1551
7
        fputs("<tr>", out);
1552
23
      if (exec_symbol_info->inputs[i] >= 0)
1553
23
      {
1554
23
        fputs("<td>", out);
1555
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]);
1556
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;
1557
23
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1558
23
        fputs("</td><td border=\"0\">=&gt; ", out);
1559
23
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
1560
23
        assert(s_idx >= 0);
1561
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);
1562
23
        if (sub_tensor_symbol->name)
1563
21
          fputs(sub_tensor_symbol->name, out);
1564
2
        else
1565
2
          fprintf(out, "tensor%d", s_idx);
1566
23
        fputs("</td></tr>", out);
1567
23
      } else {
1568
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1569
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
1570
0
        else
1571
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1572
0
      }
1573
23
    }
1574
16
  }
1575
21
  if (exec_symbol_info->output_size > 0)
1576
15
  {
1577
15
    fprintf(out, "<tr><td rowspan=\"%d\">Output</td>", exec_symbol_info->output_size);
1578
38
    for (i = 0; i < exec_symbol_info->output_size; 
i++23
)
1579
23
    {
1580
23
      if (i > 0)
1581
8
        fputs("<tr>", out);
1582
23
      if (exec_symbol_info->outputs[i] >= 0)
1583
23
      {
1584
23
        fputs("<td>", out);
1585
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]);
1586
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;
1587
23
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1588
23
        fputs("</td><td border=\"0\">=&gt; ", out);
1589
23
        const int s_idx = *(int*)ccv_array_get(tensor_symbol->s_ref, p_idx) - 1;
1590
23
        assert(s_idx >= 0);
1591
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);
1592
23
        if (sub_tensor_symbol->name)
1593
22
          fputs(sub_tensor_symbol->name, out);
1594
1
        else
1595
1
          fprintf(out, "tensor%d", s_idx);
1596
23
        fputs("</td></tr>", out);
1597
23
      } else {
1598
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1599
0
          fputs("<td colspan=\"3\">-</td></tr>", out);
1600
0
        else
1601
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1602
0
      }
1603
23
    }
1604
15
  }
1605
127
  
for (i = 0; 21
i < while_graph->tensor_symbol_info->rnum;
i++106
)
1606
106
  {
1607
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);
1608
106
    if (tensor_symbol_info->assign_ref)
1609
24
    {
1610
24
      if (flags == CCV_NNC_LONG_DOT_GRAPH)
1611
24
        fputs("<tr><td colspan=\"3\" border=\"0\">", out);
1612
0
      else
1613
0
        fputs("<tr><td colspan=\"2\" border=\"0\">", out);
1614
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);
1615
24
      if (assign_symbol_info->name)
1616
22
        fputs(assign_symbol_info->name, out);
1617
2
      else
1618
2
        fprintf(out, "tensor%d", tensor_symbol_info->assign_ref - 1);
1619
24
      fputs(" -&gt; ", out);
1620
24
      if (tensor_symbol_info->name)
1621
22
        fputs(tensor_symbol_info->name, out);
1622
2
      else
1623
2
        fprintf(out, "tensor%d", i);
1624
24
      fputs("</td></tr>", out);
1625
24
    }
1626
106
  }
1627
21
  fputs("</table>", out);
1628
21
}
1629
1630
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)
1631
11
{
1632
11
  int i;
1633
11
  if (flags == CCV_NNC_LONG_DOT_GRAPH)
1634
11
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"3\" border=\"0\"><b>", out);
1635
0
  else
1636
0
    fputs("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"><tr><td colspan=\"2\" border=\"0\"><b>", out);
1637
11
  if (exec_symbol_info->name)
1638
11
    fputs(exec_symbol_info->name, out);
1639
0
  else
1640
0
    fprintf(out, "caseof%d", index);
1641
11
  fputs(" </b>Command: ", out);
1642
11
  fputs(ccv_nnc_cmd_name(exec_symbol_info->cmd.cmd), out);
1643
11
  fputs("</td></tr>", out);
1644
11
  if (exec_symbol_info->input_size > 0)
1645
11
  {
1646
11
    fprintf(out, "<tr><td rowspan=\"%d\">Input</td>", exec_symbol_info->input_size);
1647
38
    for (i = 0; i < exec_symbol_info->input_size; 
i++27
)
1648
27
    {
1649
27
      if (i > 0)
1650
16
        fputs("<tr>", out);
1651
27
      if (exec_symbol_info->inputs[i] >= 0)
1652
27
      {
1653
27
        fputs("<td>", out);
1654
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]);
1655
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;
1656
27
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->inputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1657
27
        fputs("</td></tr>", out);
1658
27
      } else {
1659
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1660
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1661
0
        else
1662
0
          fputs("<td colspan=\"1\">-</td></tr>", out);
1663
0
      }
1664
27
    }
1665
11
  }
1666
11
  if (exec_symbol_info->output_size > 0)
1667
11
  {
1668
11
    fprintf(out, "<tr><td rowspan=\"%d\">Output</td>", exec_symbol_info->output_size);
1669
24
    for (i = 0; i < exec_symbol_info->output_size; 
i++13
)
1670
13
    {
1671
13
      if (i > 0)
1672
2
        fputs("<tr>", out);
1673
13
      if (exec_symbol_info->outputs[i] >= 0)
1674
13
      {
1675
13
        fputs("<td>", out);
1676
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]);
1677
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;
1678
13
        _ccv_nnc_symbolic_graph_dot_tensor_symbol(exec_symbol_info->outputs[i], tensor_symbol, alias_symbol, 1, flags, out);
1679
13
        fputs("</td></tr>", out);
1680
13
      } else {
1681
0
        if (flags == CCV_NNC_LONG_DOT_GRAPH)
1682
0
          fputs("<td colspan=\"2\">-</td></tr>", out);
1683
0
        else
1684
0
          fputs("<td colspan=\"1\">-</td></tr>", out);
1685
0
      }
1686
13
    }
1687
11
  }
1688
11
  fputs("</table>", out);
1689
11
}
1690
1691
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)
1692
32
{
1693
32
  int i, j, k;
1694
  // Output this node info within this subgraph.
1695
32
  if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_P_WHILE)
1696
21
  {
1697
21
    fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=<", *c, *c);
1698
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);
1699
21
    _ccv_nnc_symbolic_graph_dot_while_label(exec_symbol_info, *c, tensor_symbol_info, while_graph, flags, out);
1700
21
  } else 
if (11
exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF11
) {
1701
11
    fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=<", *c, *c);
1702
11
    _ccv_nnc_symbolic_graph_dot_case_of_label(exec_symbol_info, *c, tensor_symbol_info, flags, out);
1703
11
  }
1704
32
  fputs(">;\n", out);
1705
32
  ++(*c);
1706
81
  for (k = 0; k < exec_symbol_info->graph_ref_size; 
k++49
)
1707
49
  {
1708
49
    if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF)
1709
28
    {
1710
28
      fprintf(out, "subgraph cluster%d {\nstyle=\"rounded\";\nnode%d [style=invisible];\nlabel=\"\"\n", *c, *c);
1711
28
      ++(*c);
1712
28
    }
1713
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);
1714
49
    int* node_id = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
1715
144
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++95
)
1716
95
    {
1717
95
      node_id[i] = *c;
1718
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);
1719
      // Skip the dead one.
1720
95
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1721
2
        continue;
1722
93
      if (exec_symbol_info->graph_ref_size)
1723
3
        _ccv_nnc_symbolic_graph_dot_sub_graphs(exec_symbol_info, graph->tensor_symbol_info, graph->sub_graphs, flags, out, c);
1724
90
      else {
1725
90
        _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, *c, graph->tensor_symbol_info, flags, out);
1726
90
        ++(*c);
1727
90
      }
1728
93
    }
1729
    // Output connections.
1730
144
    for (i = 0; i < graph->exec_symbol_info->rnum; 
i++95
)
1731
95
    {
1732
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);
1733
      // Skip the dead one.
1734
95
      if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1735
2
        continue;
1736
93
      if (exec_symbol_info->outgoings)
1737
90
        
for (j = 0; 45
j < exec_symbol_info->outgoings->rnum;
j++45
)
1738
45
        {
1739
45
          const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1740
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);
1741
          // If both are sub-graphs, have both tail and head specified.
1742
45
          if (CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && 
CCV_NNC_GRAPH_REF1
(outgoing_symbol_info)[0]1
)
1743
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]);
1744
45
          else if (CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && 
!1
CCV_NNC_GRAPH_REF1
(outgoing_symbol_info)[0])
1745
1
            fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1746
44
          else if (!CCV_NNC_GRAPH_REF(exec_symbol_info)[0] && CCV_NNC_GRAPH_REF(outgoing_symbol_info)[0])
1747
3
            fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1748
41
          else
1749
41
            fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1750
45
        }
1751
93
    }
1752
49
    fputs("}\n", out);
1753
49
    ccfree(node_id);
1754
49
  }
1755
  // Extra subgraph cluster.
1756
32
  if (exec_symbol_info->flags & CCV_NNC_GRAPH_EXEC_CASE_OF)
1757
11
    fputs("}\n", out);
1758
32
}
1759
1760
void ccv_nnc_symbolic_graph_dot(const ccv_nnc_symbolic_graph_t* const graph, const int flags, FILE* out)
1761
567
{
1762
567
  fputs("digraph G {\ncompound=true;\n", out);
1763
567
  int i, j;
1764
567
  int c = 0;
1765
567
  int* node_id = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
1766
  // Output styles.
1767
1.79k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++1.23k
)
1768
1.23k
  {
1769
1.23k
    node_id[i] = c;
1770
1.23k
    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);
1771
    // Skip the dead one.
1772
1.23k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1773
430
      continue;
1774
801
    if (exec_symbol_info->graph_ref_size)
1775
29
      _ccv_nnc_symbolic_graph_dot_sub_graphs(exec_symbol_info, graph->tensor_symbol_info, graph->sub_graphs, flags, out, &c);
1776
772
    else {
1777
772
      _ccv_nnc_symbolic_graph_dot_node(exec_symbol_info, c, graph->tensor_symbol_info, flags, out);
1778
772
      ++c;
1779
772
    }
1780
801
  }
1781
  // Output connections.
1782
1.79k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++1.23k
)
1783
1.23k
  {
1784
1.23k
    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);
1785
    // Skip the dead one.
1786
1.23k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info->flags))
1787
430
      continue;
1788
801
    if (exec_symbol_info->outgoings)
1789
1.48k
      
for (j = 0; 601
j < exec_symbol_info->outgoings->rnum;
j++879
)
1790
879
      {
1791
879
        const int outgoing_idx = *(int*)ccv_array_get(exec_symbol_info->outgoings, j);
1792
879
        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);
1793
        // If both are sub-graphs, have both tail and head specified.
1794
879
        if (exec_symbol_info->graph_ref_size && 
outgoing_symbol_info->graph_ref_size16
)
1795
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]);
1796
877
        else if (exec_symbol_info->graph_ref_size && 
!outgoing_symbol_info->graph_ref_size14
)
1797
14
          fprintf(out, "node%d -> node%d [ltail=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[i]);
1798
863
        else if (!exec_symbol_info->graph_ref_size && outgoing_symbol_info->graph_ref_size)
1799
4
          fprintf(out, "node%d -> node%d [lhead=cluster%d];\n", node_id[i], node_id[outgoing_idx], node_id[outgoing_idx]);
1800
859
        else
1801
859
          fprintf(out, "node%d -> node%d;\n", node_id[i], node_id[outgoing_idx]);
1802
879
      }
1803
801
  }
1804
567
  fputs("}\n", out);
1805
567
  ccfree(node_id);
1806
567
}
1807
1808
void ccv_nnc_symbolic_graph_format(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, const ccv_nnc_symbolic_graph_format_f format_fn, void* const context)
1809
1
{
1810
1
  assert((sources && source_size) || (!sources && !source_size));
1811
1
  const ccv_nnc_graph_exec_symbol_t* const graph_sources = sources ? 
sources0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->sources, 0);
1812
1
  const int graph_source_size = source_size ? 
source_size0
: graph->sources->rnum;
1813
1
  assert((destinations && destination_size) || (!destinations && !destination_size));
1814
1
  const ccv_nnc_graph_exec_symbol_t* const graph_destinations = destinations ? 
destinations0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->destinations, 0);
1815
1
  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, 0);
1816
1
  const int graph_destination_size = destination_size ? 
destination_size0
: graph->destinations->rnum;
1817
2
  ccv_nnc_graph_visit_t* const visit = 
ccv_nnc_graph_visit_new1
(graph, exec_symbol_info, graph->exec_symbol_info->rnum, graph_sources, graph_source_size, graph_destinations, graph_destination_size, 0);
1818
0
  int outgoing_edge_count = 0;
1819
2
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node) {
1820
2
    outgoing_edge_count += node->outgoings ? 
node->outgoings->rnum1
:
01
;
1821
2
  } ccv_nnc_graph_visit_endfor
1822
2
  int* const incoming_counts = (int*)
ccmalloc1
(sizeof(int) * (graph->exec_symbol_info->rnum * 2 + outgoing_edge_count));
1823
2
  memset(incoming_counts, 0, sizeof(int) * graph->exec_symbol_info->rnum);
1824
2
  int i;
1825
2
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node) {
1826
2
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(node->flags))
1827
0
      continue;
1828
2
    if (node->outgoings && 
node->outgoings->rnum1
) {
1829
2
      for (i = 0; i < node->outgoings->rnum; 
i++1
)
1830
1
        ++incoming_counts[*(int*)ccv_array_get(node->outgoings, i)];
1831
1
    }
1832
2
  } ccv_nnc_graph_visit_endfor
1833
2
  int* const incoming_offsets = incoming_counts + graph->exec_symbol_info->rnum;
1834
2
  int incoming_edge_count = 0;
1835
2
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node, idx) {
1836
2
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(node->flags))
1837
0
      continue;
1838
2
    incoming_offsets[idx] = incoming_edge_count;
1839
2
    incoming_edge_count += incoming_counts[idx];
1840
2
  } ccv_nnc_graph_visit_endfor
1841
2
  assert(incoming_edge_count <= outgoing_edge_count);
1842
1
  memset(incoming_counts, 0, sizeof(int) * graph->exec_symbol_info->rnum);
1843
1
  int* const incoming_edges = incoming_offsets + graph->exec_symbol_info->rnum;
1844
2
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node, idx) {
1845
2
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(node->flags))
1846
0
      continue;
1847
2
    if (node->outgoings && 
node->outgoings->rnum1
) {
1848
2
      for (i = 0; i < node->outgoings->rnum; 
i++1
)
1849
1
      {
1850
1
        const int d = *(int*)ccv_array_get(node->outgoings, i);
1851
1
        incoming_edges[incoming_offsets[d] + incoming_counts[d]] = idx;
1852
1
        ++incoming_counts[d];
1853
1
      }
1854
1
    }
1855
2
  } ccv_nnc_graph_visit_endfor
1856
2
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node, idx) {
1857
2
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(node->flags))
1858
0
      continue;
1859
2
    format_fn(idx, node->name, node->cmd, node->flags, incoming_edges + incoming_offsets[idx], incoming_counts[idx], node->outgoings ? 
(int*)1
ccv_array_get1
(node->outgoings, 0) :
01
, node->outgoings ?
node->outgoings->rnum1
:
01
, node->inputs, node->input_size, node->outputs, node->output_size, context);
1860
2
  } ccv_nnc_graph_visit_endfor
1861
1
  ccv_nnc_graph_visit_free(visit);
1862
1
  ccfree(incoming_counts);
1863
1
}
1864
1865
void ccv_nnc_symbolic_graph_free(ccv_nnc_symbolic_graph_t* const graph)
1866
2.53k
{
1867
2.53k
  int i;
1868
13.8k
  for (i = 0; i < graph->exec_symbol_info->rnum; 
i++11.3k
)
1869
11.3k
    _ccv_nnc_graph_exec_symbol_free((ccv_nnc_graph_exec_symbol_info_t*)ccv_array_get(graph->exec_symbol_info, i), 0);
1870
36.4k
  for (i = 0; i < graph->tensor_symbol_info->rnum; 
i++33.9k
)
1871
33.9k
  {
1872
33.9k
    ccv_nnc_tensor_symbol_info_t* const symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, i);
1873
33.9k
    if (symbol_info->name)
1874
1.02k
      ccfree(symbol_info->name);
1875
33.9k
    if (symbol_info->s_ref)
1876
74
      ccv_array_free(symbol_info->s_ref);
1877
33.9k
  }
1878
2.53k
  if (graph->sub_graphs)
1879
29
  {
1880
80
    for (i = 0; i < graph->sub_graphs->rnum; 
i++51
)
1881
51
      ccv_nnc_symbolic_graph_free(*(ccv_nnc_symbolic_graph_t**)ccv_array_get(graph->sub_graphs, i));
1882
29
    ccv_array_free(graph->sub_graphs);
1883
29
  }
1884
2.53k
  if (graph->sources)
1885
2.46k
    ccv_array_free(graph->sources);
1886
2.53k
  if (graph->destinations)
1887
2.46k
    ccv_array_free(graph->destinations);
1888
2.53k
  if (graph->breakpoints)
1889
33
    ccfree(graph->breakpoints);
1890
2.53k
  ccv_array_free(graph->tensor_symbol_info);
1891
2.53k
  ccv_array_free(graph->exec_symbol_info);
1892
2.53k
  if (graph->backward.tensor_symbol_idx)
1893
2.32k
    ccfree(graph->backward.tensor_symbol_idx);
1894
2.53k
  if (graph->data_parallel.tensor_symbol_idx)
1895
18
    ccfree(graph->data_parallel.tensor_symbol_idx);
1896
2.53k
  if (graph->data_parallel.exec_symbol_idx)
1897
18
    ccfree(graph->data_parallel.exec_symbol_idx);
1898
2.53k
  ccfree(graph);
1899
2.53k
}
1900
1901
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)
1902
26.2k
{
1903
26.2k
  if (ccv_array_get(symbolic_graph->tensor_symbol_info, 0) != tensor_symbol_info)
1904
17.3k
    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);
1905
26.2k
  if (ccv_array_get(symbolic_graph->exec_symbol_info, 0) != exec_symbol_info)
1906
17.3k
    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);
1907
26.2k
  int i;
1908
26.2k
  if (p_tensor_symbol_info)
1909
417
    
for (i = 0; 64
i < symbolic_graph->tensor_symbol_info->rnum;
i++353
)
1910
353
      if (tensor_symbol_info[i].p_ref)
1911
132
      {
1912
132
        const int p_ref = tensor_symbol_info[i].p_ref - 1;
1913
132
        assert(p_ref < p_tensor_symbol_info_size);
1914
132
        tensor_symbol_info[i].info = p_tensor_symbol_info[p_ref].info;
1915
        // I don't need to copy over inc and ofs for alias.
1916
132
      }
1917
26.2k
  int max_input_size = 0, max_output_size = 0;
1918
  // Materialize auto hints.
1919
159k
  for (i = 0; i < symbolic_graph->exec_symbol_info->rnum; 
i++133k
)
1920
133k
  {
1921
133k
    if (CCV_NNC_GRAPH_EXEC_IS_DEAD(exec_symbol_info[i].flags))
1922
18
      continue;
1923
133k
    max_input_size = ccv_max(max_input_size, exec_symbol_info[i].input_size);
1924
133k
    max_output_size = ccv_max(max_output_size, exec_symbol_info[i].output_size);
1925
    // If there is no hint and we have input and output tensor specified.
1926
133k
    if (ccv_nnc_is_no_hint(exec_symbol_info[i].hint) &&
1927
133k
      
exec_symbol_info[i].input_size > 0110k
&&
exec_symbol_info[i].inputs[0] >= 0106k
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].inputs[0]].info)106k
&&
1928
133k
      
exec_symbol_info[i].output_size > 0106k
&&
exec_symbol_info[i].outputs[0] >= 0106k
&&
!ccv_nnc_is_tensor_auto(tensor_symbol_info[exec_symbol_info[i].outputs[0]].info)103k
)
1929
103k
      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);
1930
133k
  }
1931
1932
26.2k
  ccv_nnc_tensor_param_t input_params[ccv_max(1, max_input_size)];
1933
26.2k
  ccv_nnc_tensor_param_t output_params[ccv_max(1, max_output_size)];
1934
1935
  // Materialize auto tensors. This need to go with the topological order.
1936
  // TODO: Need to proper handle sub-graphs (thus, run sub-graph to figure out the tensor properties).
1937
113k
  ccv_nnc_graph_visit_for(visit, exec_symbol_info, node) {
1938
113k
    if (node->input_size > 0 && 
node->output_size > 0108k
)
1939
108k
    {
1940
438k
      for (i = 0; i < node->input_size; 
i++330k
)
1941
330k
        input_params[i] = node->inputs[i] >= 0 ? 
tensor_symbol_info[node->inputs[i]].info266k
:
ccv_nnc_tensor_auto63.8k
;
1942
      // output_params will be initialized to tensor_auto inside the ccv_nnc_hint_tensor_auto method.
1943
108k
      ccv_nnc_hint_tensor_auto(node->cmd, input_params, node->input_size, node->hint, output_params, node->output_size);
1944
286k
      for (i = 0; i < node->output_size; 
i++177k
)
1945
        /* Only assign the output parameters if the symbol itself is auto. */
1946
177k
        if (node->outputs[i] >= 0 && 
ccv_nnc_is_tensor_auto(tensor_symbol_info[node->outputs[i]].info)167k
)
1947
76
          tensor_symbol_info[node->outputs[i]].info = output_params[i];
1948
108k
    }
1949
113k
  } ccv_nnc_graph_visit_endfor
1950
  // If still point to any device, assign default device 00 to it.
1951
349k
  for (i = 0; i < symbolic_graph->tensor_symbol_info->rnum; 
i++323k
)
1952
323k
    if (CCV_TENSOR_GET_DEVICE(tensor_symbol_info[i].info.type) == CCV_COMPUTE_DEVICE_ANY)
1953
123k
      tensor_symbol_info[i].info.type = (~CCV_COMPUTE_DEVICE_ANY & tensor_symbol_info[i].info.type) | CCV_COMPUTE_DEVICE_000;
1954
26.2k
}
1955
1956
void ccv_nnc_symbolic_graph_tensor_auto(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)
1957
8.85k
{
1958
8.85k
  assert((sources && source_size) || (!sources && !source_size));
1959
8.85k
  const ccv_nnc_graph_exec_symbol_t* const graph_sources = sources ? 
sources0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->sources, 0);
1960
8.85k
  const int graph_source_size = source_size ? 
source_size0
: graph->sources->rnum;
1961
8.85k
  assert((destinations && destination_size) || (!destinations && !destination_size));
1962
8.85k
  const ccv_nnc_graph_exec_symbol_t* const graph_destinations = destinations ? 
destinations0
: (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(graph->destinations, 0);
1963
8.85k
  const int graph_destination_size = destination_size ? 
destination_size0
: graph->destinations->rnum;
1964
17.7k
  ccv_nnc_graph_visit_t* const visit = 
ccv_nnc_graph_visit_new8.85k
(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);
1965
8.85k
  ccv_nnc_tensor_symbol_info_t* const tensor_symbol_info = (ccv_nnc_tensor_symbol_info_t*)ccv_array_get(graph->tensor_symbol_info, 0);
1966
  // Some more clever things we can do here:
1967
  // 1. If there is a backward symbol for it, copy over the parameters.
1968
17.7k
  int i;
1969
64.2k
  for (i = 0; i < graph->backward.tensor_symbol_size; 
i++55.3k
)
1970
55.3k
  {
1971
55.3k
    const int d = graph->backward.tensor_symbol_idx[i];
1972
55.3k
    if (d >= 0)
1973
34.0k
    {
1974
34.0k
      tensor_symbol_info[d].info = tensor_symbol_info[i].info;
1975
34.0k
      memcpy(tensor_symbol_info[d].inc, tensor_symbol_info[i].inc, sizeof(tensor_symbol_info[i].inc));
1976
34.0k
      memcpy(tensor_symbol_info[d].ofs, tensor_symbol_info[i].ofs, sizeof(tensor_symbol_info[i].ofs));
1977
34.0k
    }
1978
55.3k
  }
1979
17.7k
  ccv_nnc_graph_exec_symbol_info_t* const exec_symbol_info = (ccv_nnc_graph_exec_symbol_info_t*)
ccv_array_get8.85k
(graph->exec_symbol_info, 0);
1980
  // 2. If there is a copy (because the data parallel setting), copy over the info.
1981
17.7k
  const int parallel_count = graph->data_parallel.count;
1982
17.7k
  if (
parallel_count > 18.85k
)
1983
18
  {
1984
18
    int j;
1985
17.9k
    for (i = 0; i < graph->data_parallel.tensor_symbol_size; 
i++17.9k
)
1986
71.6k
      
for (j = 0; 17.9k
j < parallel_count - 1;
j++53.7k
)
1987
53.7k
      {
1988
53.7k
        const int d = graph->data_parallel.tensor_symbol_idx[i * (parallel_count - 1) + j];
1989
53.7k
        if (d >= 0)
1990
19.9k
        {
1991
19.9k
          tensor_symbol_info[d].info = tensor_symbol_info[i].info;
1992
19.9k
          CCV_TENSOR_SET_DEVICE_ID(tensor_symbol_info[d].info.type, j + 1); // Set the device id.
1993
19.9k
          memcpy(tensor_symbol_info[d].inc, tensor_symbol_info[i].inc, sizeof(tensor_symbol_info[i].inc));
1994
19.9k
          memcpy(tensor_symbol_info[d].ofs, tensor_symbol_info[i].ofs, sizeof(tensor_symbol_info[i].ofs));
1995
19.9k
        }
1996
53.7k
      }
1997
2.43k
    for (i = 0; i < graph->data_parallel.exec_symbol_size; 
i++2.41k
)
1998
9.64k
      
for (j = 0; 2.41k
j < parallel_count - 1;
j++7.23k
)
1999
7.23k
      {
2000
7.23k
        const int d = graph->data_parallel.exec_symbol_idx[i * (parallel_count - 1) + j];
2001
7.23k
        if (d >= 0)
2002
7.23k
          exec_symbol_info[d].cmd = exec_symbol_info[i].cmd;
2003
7.23k
      }
2004
18
  }
2005
17.7k
  ccv_nnc_symbolic_graph_symbol_infer(graph, visit, graph_sources, graph_source_size, graph_destinations, graph_destination_size, 0, 0, tensor_symbol_info, exec_symbol_info);
2006
17.7k
  ccv_nnc_graph_visit_free(visit);
2007
17.7k
}
2008
2009
void ccv_nnc_symbolic_graph_sources_to_destinations(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, uint64_t* const bitmask)
2010
13
{
2011
13
  assert(sources && source_size);
2012
13
  assert(destinations && destination_size);
2013
13
  int i;
2014
45
  for (i = 0; i < source_size; 
i++32
)
2015
32
  {
2016
32
    assert(sources[i].graph == graph);
2017
32
    assert(sources[i].d >= 0 && sources[i].d < graph->exec_symbol_info->rnum);
2018
32
  }
2019
26
  
for (i = 0; 13
i < destination_size;
i++13
)
2020
13
  {
2021
13
    assert(destinations[i].graph == graph);
2022
13
    assert(destinations[i].d >= 0 && destinations[i].d < graph->exec_symbol_info->rnum);
2023
13
  }
2024
13
  ccv_sparse_matrix_t* const exec_dep = ccv_sparse_matrix_new(graph->exec_symbol_info->rnum, graph->exec_symbol_info->rnum, CCV_8U | CCV_C1, CCV_SPARSE_ROW_MAJOR, 0);
2025
13
  ccv_array_t* const ws = ccv_array_new(sizeof(int), source_size, 0);
2026
45
  for (i = 0; i < source_size; 
i++32
)
2027
32
    ccv_array_push(ws, &sources[i].d);
2028
13
  int* buf = (int*)ccmalloc(sizeof(int) * graph->exec_symbol_info->rnum);
2029
13
  int buf_size;
2030
13
#define for_block(x, val) \
2031
15
  do { \
2032
15
    if (((uint8_t*)val)[0] != 0) \
2033
15
      buf[buf_size++] = x; \
2034
15
  } while (0)
2035
13
  const uint8_t one = 1;
2036
55
  for (i = 0; i < ws->rnum; 
i++42
)
2037
42
  {
2038
42
    int j;
2039
42
    const int d = *(int*)ccv_array_get(ws, i);
2040
42
    int flag = 0;
2041
84
    for (j = 0; !flag && 
j < destination_size79
;
j++42
)
2042
42
      flag = (d == destinations[j].d);
2043
42
    if (flag)
2044
5
      continue;
2045
37
    buf_size = 0; /* save all its parent deps to this buffer */
2046
37
    ccv_sparse_matrix_vector_t* vector = ccv_get_sparse_matrix_vector(exec_dep, d);
2047
37
    if (vector)
2048
15
      
CCV_SPARSE_VECTOR_FOREACH11
(exec_dep, vector, for_block);
2049
37
    ccv_nnc_graph_exec_symbol_info_t* const info = ccv_array_get(graph->exec_symbol_info, d);
2050
37
    if (info->outgoings && 
info->outgoings->rnum > 028
)
2051
25
    {
2052
25
      ccv_array_t* const outgoings = info->outgoings;
2053
50
      for (j = 0; j < outgoings->rnum; 
j++25
)
2054
25
      {
2055
25
        const int outgoing_d = *(int*)ccv_array_get(outgoings, j);
2056
25
        int k;
2057
25
        int flag = 0;
2058
50
        for (k = 0; !flag && 
k < destination_size35
;
k++25
)
2059
25
          flag = (outgoing_d == destinations[k].d);
2060
        // We cannot avoid the ones that visited, because these may not contain all the deps.
2061
25
        if (!flag)
2062
10
          ccv_array_push(ws, &outgoing_d);
2063
25
        ccv_set_sparse_matrix_cell(exec_dep, outgoing_d, d, &one);
2064
31
        for (k = 0; k < buf_size; 
k++6
)
2065
6
          ccv_set_sparse_matrix_cell(exec_dep, outgoing_d, buf[k], &one);
2066
25
      }
2067
25
    }
2068
37
  }
2069
13
  ccfree(buf);
2070
13
  ccv_array_free(ws);
2071
  // Use exec_dep to fill the bitmask
2072
45
  for (i = 0; i < source_size; 
i++32
)
2073
32
  {
2074
32
    const int d = sources[i].d;
2075
32
    int j;
2076
32
    int flag = 0;
2077
64
    for (j = 0; !flag && 
j < destination_size44
;
j++32
)
2078
32
      if (d == destinations[j].d) {
2079
5
        flag = 1;
2080
27
      } else {
2081
27
        ccv_numeric_data_t cell = ccv_get_sparse_matrix_cell(exec_dep, destinations[j].d, d);
2082
27
        flag = (cell.u8 && 
cell.u8[0] != 015
);
2083
27
      }
2084
32
    if (flag)
2085
20
      bitmask[i >> 6] |= ((uint64_t)1 << (i & 63));
2086
12
    else
2087
12
      bitmask[i >> 6] &= ~((uint64_t)1 << (i & 63));
2088
32
  }
2089
13
  ccv_matrix_free(exec_dep);
2090
13
}