Coverage Report

Created: 2019-07-03 22:50

/home/liu/buildslave/linux-x64-runtests/build/lib/nnc/ccv_nnc_dynamic_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_nnc_easy.h"
5
#include "ccv_internal.h"
6
#include "_ccv_nnc_dynamic_graph.h"
7
8
#pragma mark - Level-4 API
9
10
ccv_nnc_dynamic_graph_t* ccv_nnc_dynamic_graph_new(void)
11
14
{
12
14
  ccv_nnc_dynamic_graph_t* graph = ccmalloc(sizeof(ccv_nnc_dynamic_graph_t));
13
14
  graph->reuse_var = -1;
14
14
  graph->vars = ccv_array_new(sizeof(ccv_nnc_tensor_variable_t), 1, 0);
15
14
  graph->binds = ccv_array_new(sizeof(ccv_nnc_tensor_variable_graph_bind_t), 1, 0);
16
14
  graph->tape = ccv_nnc_symbolic_graph_new();
17
14
  graph->ws = 0;
18
14
  return graph;
19
14
}
20
21
static void _ccv_nnc_tensor_variable_free(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable, const int zeroing)
22
6.08k
{
23
6.08k
  const int index = tensor_variable->index;
24
6.08k
  if (tensor_variable->tensor_view && 
!3.07k
CCV_NNC_IS_EXTERN_TENSOR_VIEW3.07k
(tensor_variable->tensor_view))
25
6.08k
  {
26
3.06k
    if (CCV_IS_TENSOR_VIEW(tensor_variable->tensor_view))
27
3.06k
      
ccv_nnc_tensor_view_free(tensor_variable->tensor_view)6
;
28
3.05k
    else
29
3.05k
      ccv_nnc_tensor_free((ccv_nnc_tensor_t*)tensor_variable->tensor_view);
30
3.06k
  }
31
6.08k
  ccfree(tensor_variable);
32
6.08k
  if (zeroing)
33
6.02k
    *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, index) = 0;
34
6.08k
  int i;
35
12.1k
  for (i = graph->vars->rnum - 1; i >= 0; 
i--6.01k
)
36
12.1k
    if (*(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, i) != 0)
37
6.08k
    {
38
6.08k
      graph->vars->rnum = i + 1;
39
6.08k
      break;
40
6.08k
    }
41
6.08k
  if (index < graph->vars->rnum &&
42
6.08k
    
(4.07k
index < graph->reuse_var4.07k
||
graph->reuse_var < 04.07k
))
43
1.02k
    graph->reuse_var = index;
44
5.06k
  else if (graph->reuse_var >= graph->vars->rnum)
45
1.00k
    graph->reuse_var = -1;
46
6.08k
}
47
48
static void _ccv_nnc_tensor_variable_graph_bind_free(ccv_nnc_tensor_variable_graph_bind_t* const bind, const int zeroing)
49
6.09k
{
50
6.09k
  bind->index = CCV_NNC_TENSOR_NO_VARIABLE;
51
6.09k
  if (bind->sources)
52
3.04k
    ccv_array_free(bind->sources);
53
6.09k
  if (bind->destinations)
54
5.05k
    ccv_array_free(bind->destinations);
55
6.09k
  if (bind->tensor_view && 
!3.01k
CCV_NNC_IS_EXTERN_TENSOR_VIEW3.01k
(bind->tensor_view))
56
6.09k
  {
57
3.01k
    if (CCV_IS_TENSOR_VIEW(bind->tensor_view))
58
3.01k
      
ccv_nnc_tensor_view_free(bind->tensor_view)0
;
59
3.01k
    else
60
3.01k
      ccv_nnc_tensor_free((ccv_nnc_tensor_t*)bind->tensor_view);
61
3.01k
  }
62
6.09k
  if (zeroing)
63
6.01k
  {
64
6.01k
    bind->sources = 0;
65
6.01k
    bind->destinations = 0;
66
6.01k
    bind->tensor_view = 0;
67
6.01k
  }
68
6.09k
}
69
70
void ccv_nnc_dynamic_graph_free(ccv_nnc_dynamic_graph_t* const graph)
71
14
{
72
14
  int i;
73
75
  for (i = 0; i < graph->vars->rnum; 
i++61
)
74
61
  {
75
61
    ccv_nnc_tensor_variable_t tensor_variable = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, i);
76
61
    if (tensor_variable)
77
60
      _ccv_nnc_tensor_variable_free(graph, tensor_variable, 0);
78
61
  }
79
14
  ccv_array_free(graph->vars);
80
85
  for (i = 0; i < graph->binds->rnum; 
i++71
)
81
71
    _ccv_nnc_tensor_variable_graph_bind_free((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, i), 0);
82
14
  ccv_array_free(graph->binds);
83
14
  ccv_nnc_symbolic_graph_free(graph->tape);
84
14
  if (graph->ws)
85
8
    ccv_array_free(graph->ws);
86
14
  ccfree(graph);
87
14
}
88
89
void ccv_nnc_tensor_variable_set(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable, ccv_nnc_tensor_t* const tensor)
90
9
{
91
9
  assert(!tensor_variable->tensor_view);
92
9
  assert(!tensor_variable->alias_ref);
93
9
  tensor_variable->info = tensor->info;
94
9
  tensor_variable->tensor_view = (ccv_nnc_tensor_view_t*)((uintptr_t)tensor | 1);
95
9
}
96
97
inline static void _ccv_nnc_tensor_variable_init(ccv_nnc_dynamic_graph_t* const graph, ccv_nnc_tensor_variable_t tensor_variable, const ccv_nnc_tensor_param_t info)
98
6.08k
{
99
6.08k
  tensor_variable->alias_ref = 0;
100
6.08k
  tensor_variable->info = info;
101
6.08k
  tensor_variable->symbol = NO_TENSOR_SYMBOL;
102
6.08k
  tensor_variable->tensor_view = 0;
103
6.08k
  if (graph->reuse_var >= 0)
104
12
  {
105
12
    const int reuse_var = graph->reuse_var;
106
12
    assert(reuse_var < graph->vars->rnum);
107
12
    tensor_variable->index = reuse_var;
108
12
    *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, reuse_var) = tensor_variable;
109
12
    int i;
110
12
    graph->reuse_var = -1;
111
33
    for (i = reuse_var + 1; i < graph->vars->rnum && 
graph->reuse_var < 021
;
i++21
)
112
21
      if (*(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, i) == 0)
113
0
        graph->reuse_var = i;
114
6.07k
  } else {
115
6.07k
    tensor_variable->index = graph->vars->rnum;
116
6.07k
    ccv_array_push(graph->vars, &tensor_variable);
117
6.07k
  }
118
6.08k
}
119
120
ccv_nnc_tensor_variable_t ccv_nnc_tensor_variable_new_impl(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_param_t info)
121
6.08k
{
122
6.08k
  ccv_nnc_tensor_variable_t tensor_variable = ccmalloc(sizeof(struct ccv_nnc_tensor_variable_s));
123
6.08k
  tensor_variable->type = CCV_NNC_TENSOR_VARIABLE;
124
6.08k
  _ccv_nnc_tensor_variable_init(graph, tensor_variable, info);
125
6.08k
  return tensor_variable;
126
6.08k
}
127
128
ccv_nnc_tensor_variable_t ccv_nnc_tensor_constant_new_impl(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_param_t info)
129
2
{
130
2
  ccv_nnc_tensor_variable_t tensor_variable = ccmalloc(sizeof(struct ccv_nnc_tensor_variable_s));
131
2
  tensor_variable->type = CCV_NNC_TENSOR_CONSTANT;
132
2
  _ccv_nnc_tensor_variable_init(graph, tensor_variable, info);
133
2
  return tensor_variable;
134
2
}
135
136
ccv_nnc_tensor_variable_t ccv_nnc_tensor_variable_alias_new(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable, const int ofs[CCV_NNC_MAX_DIM_ALLOC], const int inc[CCV_NNC_MAX_DIM_ALLOC], const ccv_nnc_tensor_param_t info)
137
6
{
138
6
  assert(!tensor_variable->alias_ref);
139
6
  ccv_nnc_tensor_variable_t variable_alias = ccmalloc(sizeof(struct ccv_nnc_tensor_variable_s));
140
6
  variable_alias->type = tensor_variable->type;
141
6
  variable_alias->alias_ref = tensor_variable->index + 1;
142
6
  variable_alias->info = info;
143
6
  variable_alias->symbol = NO_TENSOR_SYMBOL;
144
6
  variable_alias->tensor_view = 0;
145
6
  memcpy(variable_alias->ofs, ofs, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
146
6
  memcpy(variable_alias->inc, inc, sizeof(int) * CCV_NNC_MAX_DIM_ALLOC);
147
6
  if (graph->reuse_var >= 0)
148
0
  {
149
0
    const int reuse_var = graph->reuse_var;
150
0
    assert(reuse_var < graph->vars->rnum);
151
0
    variable_alias->index = reuse_var;
152
0
    *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, reuse_var) = variable_alias;
153
0
    int i;
154
0
    graph->reuse_var = -1;
155
0
    for (i = reuse_var + 1; i < graph->vars->rnum && graph->reuse_var < 0; i++)
156
0
      if (*(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, i) == 0)
157
0
        graph->reuse_var = i;
158
6
  } else {
159
6
    variable_alias->index = graph->vars->rnum;
160
6
    ccv_array_push(graph->vars, &variable_alias);
161
6
  }
162
6
  return variable_alias;
163
6
}
164
165
ccv_nnc_tensor_t* ccv_nnc_tensor_from_variable(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable)
166
13.3k
{
167
13.3k
  if (tensor_variable->tensor_view)
168
7.24k
  {
169
7.24k
    if (tensor_variable->alias_ref)
170
0
    {
171
0
      const int alias_ref = tensor_variable->alias_ref - 1;
172
0
      assert(alias_ref >= 0);
173
0
      ccv_nnc_tensor_variable_t variable_to = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, alias_ref);
174
0
      ccv_nnc_tensor_view_t* const tv = tensor_variable->tensor_view; // We cannot have an alias with custom set tensor, otherwise the pointer update is invalid.
175
0
      assert(!CCV_NNC_IS_EXTERN_TENSOR_VIEW(tv));
176
0
      // Update the tensor_view pointer every time access it, because the underlying variable it alias to have changed.
177
0
      tv->data.u8 = CCV_NNC_TENSOR_VIEW(variable_to->tensor_view)->data.u8 + tv->off;
178
0
    }
179
7.24k
    return (ccv_nnc_tensor_t*)CCV_NNC_TENSOR_VIEW(tensor_variable->tensor_view);
180
6.07k
  }
181
6.07k
  if (!tensor_variable->alias_ref)
182
6.07k
  {
183
6.07k
    tensor_variable->tensor_view = (ccv_nnc_tensor_view_t*)ccv_nnc_tensor_new(0, tensor_variable->info, 0);
184
6.07k
    return (ccv_nnc_tensor_t*)tensor_variable->tensor_view;
185
6.07k
  }
186
6
  const int alias_ref = tensor_variable->alias_ref - 1;
187
6
  assert(alias_ref >= 0);
188
6
  ccv_nnc_tensor_variable_t variable_to = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, alias_ref);
189
6
  assert(!variable_to->alias_ref);
190
6
  if (!variable_to->tensor_view)
191
0
    variable_to->tensor_view = (ccv_nnc_tensor_view_t*)ccv_nnc_tensor_new(0, variable_to->info, 0);
192
6
  tensor_variable->tensor_view = ccv_nnc_tensor_view_new((ccv_nnc_tensor_t*)CCV_NNC_TENSOR_VIEW(variable_to->tensor_view), tensor_variable->info.dim, tensor_variable->ofs, tensor_variable->inc);
193
6
  return (ccv_nnc_tensor_t*)tensor_variable->tensor_view;
194
6
}
195
196
static void _ccv_nnc_tensor_symbol_extra_new(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable, const ccv_nnc_tensor_symbol_t symbol)
197
6.07k
{
198
6.07k
  if (symbol.d >= graph->binds->rnum)
199
71
  {
200
71
    const int rnum = graph->binds->rnum;
201
71
    ccv_array_resize(graph->binds, symbol.d + 1);
202
71
    int i;
203
142
    for (i = rnum; i < graph->binds->rnum; 
i++71
)
204
71
      ((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, i))->index = CCV_NNC_TENSOR_NO_VARIABLE;
205
71
  }
206
6.07k
  ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, symbol.d);
207
6.07k
  bind->type = tensor_variable->type;
208
6.07k
  bind->index = tensor_variable->index;
209
6.07k
  if (bind->sources)
210
0
    ccv_array_free(bind->sources);
211
6.07k
  bind->sources = 0;
212
6.07k
  if (bind->destinations)
213
0
    ccv_array_free(bind->destinations);
214
6.07k
  bind->destinations = 0;
215
6.07k
  bind->tensor_view = 0;
216
6.07k
}
217
218
static ccv_nnc_tensor_symbol_t _ccv_nnc_tensor_symbol_from_variable(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable)
219
9.13k
{
220
9.13k
  if (tensor_variable->symbol.d >= 0)
221
3.06k
    return tensor_variable->symbol;
222
6.07k
  if (!tensor_variable->alias_ref)
223
6.07k
  {
224
6.07k
    const ccv_nnc_tensor_symbol_t symbol = tensor_variable->symbol = ccv_nnc_tensor_symbol_new(graph->tape, tensor_variable->info, 0);
225
6.07k
    _ccv_nnc_tensor_symbol_extra_new(graph, tensor_variable, symbol);
226
6.07k
    return symbol;
227
6.07k
  }
228
6
  const int alias_ref = tensor_variable->alias_ref - 1;
229
6
  assert(alias_ref >= 0);
230
6
  ccv_nnc_tensor_variable_t variable_to = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, alias_ref);
231
6
  assert(!variable_to->alias_ref);
232
6
  const ccv_nnc_tensor_symbol_t symbol = tensor_variable->symbol = ccv_nnc_tensor_symbol_alias_new(graph->tape, _ccv_nnc_tensor_symbol_from_variable(graph, variable_to), tensor_variable->ofs, tensor_variable->inc, tensor_variable->info, 0);
233
6
  _ccv_nnc_tensor_symbol_extra_new(graph, tensor_variable, symbol);
234
6
  return symbol;
235
6
}
236
237
// Return the tensor variable that is old (the provided tensor variable will have a new setting).
238
ccv_nnc_tensor_variable_t ccv_nnc_tensor_variable_exchange_new(ccv_nnc_dynamic_graph_t* const graph, ccv_nnc_tensor_variable_t tensor_variable)
239
1.00k
{
240
1.00k
  struct ccv_nnc_tensor_variable_s x = *tensor_variable;
241
1.00k
  ccv_nnc_tensor_variable_t new_variable;
242
1.00k
  // Need to handle alias.
243
1.00k
  if (x.alias_ref)
244
0
    new_variable = ccv_nnc_tensor_variable_alias_new(graph, *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, x.alias_ref - 1), x.ofs, x.inc, x.info);
245
1.00k
  else
246
1.00k
    new_variable = ccv_nnc_tensor_variable_new(graph, x.info);
247
1.00k
  *tensor_variable = *new_variable;
248
1.00k
  *new_variable = x;
249
1.00k
  // The index should be the same though.
250
1.00k
  const int index = new_variable->index;
251
1.00k
  new_variable->index = tensor_variable->index;
252
1.00k
  if (new_variable->symbol.d != CCV_NNC_NO_TENSOR_SYMBOL)
253
1.00k
  {
254
1.00k
    ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, new_variable->symbol.d);
255
1.00k
    bind->index = new_variable->index;
256
1.00k
  }
257
1.00k
  tensor_variable->index = index;
258
1.00k
  return new_variable;
259
1.00k
}
260
261
int ccv_nnc_dynamic_graph_exec(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, const ccv_nnc_tensor_variable_t* const inputs, const int input_size, ccv_nnc_tensor_variable_t* const outputs, const int output_size)
262
3.04k
{
263
3.04k
  int i, j;
264
9.13k
  for (i = 0; i < input_size; 
i++6.08k
)
265
6.08k
    if (inputs[i] && 
!inputs[i]->alias_ref6.08k
)
266
6.08k
      { assert(inputs[i]->tensor_view); }
267
3.04k
  ccv_nnc_tensor_t* input_tensors[ccv_max(1, input_size)];
268
9.13k
  for (i = 0; i < input_size; 
i++6.08k
)
269
6.08k
    input_tensors[i] = inputs[i] ? 
ccv_nnc_tensor_from_variable(graph, inputs[i])6.08k
:
01
;
270
3.04k
  ccv_nnc_tensor_symbol_t input_symbols[ccv_max(1, input_size)];
271
9.13k
  for (i = 0; i < input_size; 
i++6.08k
)
272
6.08k
    input_symbols[i] = inputs[i] ? 
_ccv_nnc_tensor_symbol_from_variable(graph, inputs[i])6.08k
:
NO_TENSOR_SYMBOL1
;
273
3.04k
  ccv_array_t* input_sources[ccv_max(1, input_size)];
274
3.04k
  ccv_array_t* input_alias_sources[ccv_max(1, input_size)];
275
9.13k
  for (i = 0; i < input_size; 
i++6.08k
)
276
6.08k
  {
277
6.08k
    input_sources[i] = input_symbols[i].d != CCV_NNC_NO_TENSOR_SYMBOL ? 
((ccv_nnc_tensor_variable_graph_bind_t*)6.08k
ccv_array_get6.08k
(graph->binds, input_symbols[i].d))->sources :
01
;
278
6.08k
    if (inputs[i] && 
inputs[i]->alias_ref6.08k
)
279
3
    {
280
3
      const int alias_ref = outputs[i]->alias_ref - 1;
281
3
      assert(alias_ref >= 0);
282
3
      ccv_nnc_tensor_variable_t variable_to = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, alias_ref);
283
3
      input_alias_sources[i] = ((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, variable_to->symbol.d))->sources;
284
3
    } else
285
6.08k
      input_alias_sources[i] = 0;
286
6.08k
  }
287
3.04k
  int output_auto = 0;
288
6.09k
  for (i = 0; !output_auto && 
i < output_size3.05k
;
i++3.04k
)
289
3.04k
    output_auto = outputs[i] ? ccv_nnc_is_tensor_auto(outputs[i]->info) : 
00
;
290
3.04k
  // One extra step, infer the parameters for outputs.
291
3.04k
  if (output_auto)
292
3.03k
  {
293
3.03k
    ccv_nnc_tensor_param_t input_params[ccv_max(1, input_size)];
294
9.10k
    for (i = 0; i < input_size; 
i++6.06k
)
295
6.06k
      input_params[i] = inputs[i] ? 
inputs[i]->info6.06k
:
ccv_nnc_tensor_auto1
;
296
3.03k
    ccv_nnc_tensor_param_t output_params[ccv_max(1, output_size)];
297
6.07k
    for (i = 0; i < output_size; 
i++3.04k
)
298
3.04k
      output_params[i] = outputs[i] ? 
outputs[i]->info3.03k
:
ccv_nnc_tensor_auto1
;
299
3.03k
    ccv_nnc_hint_tensor_auto(cmd, input_params, input_size, hint, output_params, output_size);
300
6.07k
    for (i = 0; i < output_size; 
i++3.04k
)
301
3.04k
      if (outputs[i])
302
3.03k
        outputs[i]->info = output_params[i];
303
3.03k
  }
304
3.04k
  int freeable_size = 0;
305
3.04k
  ccv_nnc_tensor_variable_t freeables[ccv_max(1, output_size)];
306
3.04k
  // Refresh the symbol if it is binded to an existing exec. Otherwise we cannot keep the SSA guarantee.
307
6.09k
  for (i = 0; i < output_size; 
i++3.05k
)
308
3.05k
  {
309
3.05k
    // First, go over to see whether there is enforce inplace.
310
3.05k
    int enforce_idx = -1;
311
9.15k
    for (j = 0; enforce_idx < 0 && 
j < input_size9.15k
;
j++6.10k
)
312
6.10k
      if (inputs[j] && 
ccv_nnc_cmd_enforce_inplace(cmd, j, input_size, i, output_size)6.10k
)
313
2
        enforce_idx = j;
314
3.05k
    if (enforce_idx >= 0)
315
2
      { assert(outputs[i] == inputs[enforce_idx] && outputs[i]->symbol.d != CCV_NNC_NO_TENSOR_SYMBOL); }
316
3.05k
    if (outputs[i] && 
outputs[i]->symbol.d != CCV_NNC_NO_TENSOR_SYMBOL3.05k
)
317
5
    {
318
5
      const ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, outputs[i]->symbol.d);
319
5
      if (enforce_idx >= 0)
320
2
        { assert(!bind->destinations || bind->destinations->rnum == 0); }
321
5
      if (bind->sources && 
bind->sources->rnum > 03
)
322
3
      {
323
3
        const ccv_nnc_tensor_variable_t old_var = freeables[freeable_size++] = ccv_nnc_tensor_variable_exchange_new(graph, outputs[i]);
324
3
        // If this is enforce output, make sure the tensor view is taken by the output.
325
3
        if (enforce_idx >= 0)
326
0
        {
327
0
          outputs[i]->tensor_view = old_var->tensor_view; // Make sure the tensor view is taken over by the output.
328
0
          old_var->tensor_view = 0;
329
0
        }
330
3
      }
331
5
    }
332
3.05k
  }
333
3.04k
  ccv_nnc_tensor_t* output_tensors[ccv_max(1, output_size)];
334
6.09k
  for (i = 0; i < output_size; 
i++3.05k
)
335
3.05k
    output_tensors[i] = outputs[i] ? 
ccv_nnc_tensor_from_variable(graph, outputs[i])3.05k
:
01
;
336
3.04k
  ccv_nnc_cmd_exec(cmd, hint, flags, input_tensors, input_size, output_tensors, output_size, 0);
337
3.04k
  if (input_size > 0) // No need to record the execution if there is no input.
338
3.04k
  {
339
3.04k
    ccv_nnc_tensor_symbol_t output_symbols[ccv_max(1, output_size)];
340
6.09k
    for (i = 0; i < output_size; 
i++3.04k
)
341
3.04k
      if (outputs[i])
342
3.04k
      {
343
3.04k
        assert(outputs[i]->type != CCV_NNC_TENSOR_CONSTANT);
344
3.04k
        output_symbols[i] = _ccv_nnc_tensor_symbol_from_variable(graph, outputs[i]);
345
3.04k
      } else
346
1
        output_symbols[i] = NO_TENSOR_SYMBOL;
347
3.04k
    ccv_nnc_graph_exec_symbol_t graph_exec = ccv_nnc_graph_exec_symbol_new(graph->tape, cmd, input_symbols, input_size, output_symbols, output_size, 0);
348
3.04k
    // This needs to be done before we set the new sources on the outputs.
349
9.12k
    for (i = 0; i < input_size; 
i++6.08k
)
350
6.08k
    {
351
6.08k
      if (input_sources[i])
352
6.05k
        
for (j = 0; 3.03k
j < input_sources[i]->rnum;
j++3.02k
)
353
3.02k
          ccv_nnc_graph_exec_symbol_concat(graph->tape, (ccv_nnc_graph_exec_symbol_t){
354
3.02k
            .d = *(int*)ccv_array_get(input_sources[i], j),
355
3.02k
            .graph = graph->tape
356
3.02k
          }, graph_exec);
357
6.08k
      if (input_alias_sources[i])
358
5
        
for (j = 0; 2
j < input_alias_sources[i]->rnum;
j++3
)
359
3
          ccv_nnc_graph_exec_symbol_concat(graph->tape, (ccv_nnc_graph_exec_symbol_t){
360
3
            .d = *(int*)ccv_array_get(input_alias_sources[i], j),
361
3
            .graph = graph->tape
362
3
          }, graph_exec);
363
6.08k
    }
364
9.12k
    for (i = 0; i < input_size; 
i++6.08k
)
365
6.08k
    {
366
6.08k
      if (!inputs[i])
367
1
        continue;
368
6.08k
      ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, inputs[i]->symbol.d);
369
6.08k
      if (!bind->destinations)
370
5.05k
        bind->destinations = ccv_array_new(sizeof(int), 1, 0);
371
6.08k
      ccv_array_add_unique_int(bind->destinations, graph_exec.d);
372
6.08k
    }
373
6.09k
    for (i = 0; i < output_size; 
i++3.04k
)
374
3.04k
    {
375
3.04k
      if (!outputs[i])
376
1
        continue;
377
3.04k
      ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, outputs[i]->symbol.d);
378
3.04k
      assert(!bind->sources); // This is a new symbol, therefore, no binded sources associated yet.
379
3.04k
      bind->sources = ccv_array_new(sizeof(int), 1, 0);
380
3.04k
      ccv_array_add_unique_int(bind->sources, graph_exec.d);
381
3.04k
      if (outputs[i]->alias_ref)
382
3
      {
383
3
          const int alias_ref = outputs[i]->alias_ref - 1;
384
3
          assert(alias_ref >= 0);
385
3
          ccv_nnc_tensor_variable_t variable_to = *(ccv_nnc_tensor_variable_t*)ccv_array_get(graph->vars, alias_ref);
386
3
          ccv_nnc_tensor_variable_graph_bind_t* const bind_to = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, variable_to->symbol.d);
387
3
          if (!bind_to->sources)
388
1
            bind_to->sources = ccv_array_new(sizeof(int), 1, 0);
389
3
          ccv_array_add_unique_int(bind_to->sources, graph_exec.d);
390
3
      }
391
3.04k
    }
392
3.04k
  }
393
3.04k
  // Now, able to free some of the reused outputs.
394
3.05k
  
for (i = 0; 3.04k
i < freeable_size;
i++3
)
395
3
    ccv_nnc_tensor_variable_free(graph, freeables[i]);
396
3.04k
  return CCV_NNC_EXEC_SUCCESS;
397
3.04k
}
398
399
static void _ccv_nnc_update_bind_destinations_when_free(ccv_nnc_symbolic_graph_t* const graph, const int freed_symbol_d, ccv_nnc_tensor_variable_graph_bind_t* const bind, const int tensor_index)
400
6.03k
{
401
6.03k
  int i;
402
6.03k
  if (bind->destinations)
403
5.03k
  {
404
5.03k
    int flag = 0;
405
8.08k
    for (i = 0; !flag && 
i < bind->destinations->rnum5.06k
;
i++3.04k
)
406
3.04k
    {
407
3.04k
      const int symbol_d = *(int*)ccv_array_get(bind->destinations, i);
408
3.04k
      if (symbol_d == freed_symbol_d)
409
3.02k
      {
410
3.02k
        if (i < bind->destinations->rnum - 1)
411
2
          *(int*)ccv_array_get(bind->destinations, i) = *(int*)ccv_array_get(bind->destinations, bind->destinations->rnum - 1);
412
3.02k
        --bind->destinations->rnum;
413
3.02k
        flag = 1;
414
3.02k
      }
415
3.04k
    }
416
5.03k
    // This symbol can be freed.
417
5.03k
    if (flag && 
bind->index < 03.02k
&&
418
5.03k
      
(3.00k
!bind->sources3.00k
||
bind->sources->rnum == 01.00k
) &&
419
5.03k
      
(3.00k
!bind->destinations3.00k
||
bind->destinations->rnum == 03.00k
))
420
3.00k
    {
421
3.00k
      _ccv_nnc_tensor_variable_graph_bind_free(bind, 1);
422
3.00k
      ccv_nnc_tensor_symbol_free(graph, (ccv_nnc_tensor_symbol_t){
423
3.00k
        .d = tensor_index,
424
3.00k
        .graph = graph
425
3.00k
      });
426
3.00k
    }
427
5.03k
  }
428
6.03k
}
429
430
static void _ccv_nnc_update_bind_sources_when_free(ccv_nnc_symbolic_graph_t* const graph, const int freed_symbol_d, ccv_nnc_tensor_variable_graph_bind_t* const bind, const int tensor_index)
431
3.02k
{
432
3.02k
  int i;
433
3.02k
  if (bind->sources)
434
3.02k
  {
435
3.02k
    int flag = 0;
436
6.04k
    for (i = 0; !flag && 
i < bind->sources->rnum3.02k
;
i++3.02k
)
437
3.02k
    {
438
3.02k
      const int symbol_d = *(int*)ccv_array_get(bind->sources, i);
439
3.02k
      if (symbol_d == freed_symbol_d)
440
3.02k
      {
441
3.02k
        if (i < bind->sources->rnum - 1)
442
0
          *(int*)ccv_array_get(bind->sources, i) = *(int*)ccv_array_get(bind->sources, bind->sources->rnum - 1);
443
3.02k
        --bind->sources->rnum;
444
3.02k
        flag = 1;
445
3.02k
      }
446
3.02k
    }
447
3.02k
    // This symbol can be freed.
448
3.02k
    if (flag && bind->index < 0 &&
449
3.02k
      
(1.00k
!bind->sources1.00k
||
bind->sources->rnum == 01.00k
) &&
450
3.02k
      
(1.00k
!bind->destinations1.00k
||
bind->destinations->rnum == 01.00k
))
451
1
    {
452
1
      _ccv_nnc_tensor_variable_graph_bind_free(bind, 1);
453
1
      ccv_nnc_tensor_symbol_free(graph, (ccv_nnc_tensor_symbol_t){
454
1
        .d = tensor_index,
455
1
        .graph = graph
456
1
      });
457
1
    }
458
3.02k
  }
459
3.02k
}
460
461
static void _ccv_nnc_update_bind_sources_destinations_when_free(ccv_nnc_symbolic_graph_t* const graph, const int freed_symbol_d, ccv_array_t* const binds, const int* const inputs, const int input_size, const int* const outputs, const int output_size)
462
3.02k
{
463
3.02k
  int i;
464
9.05k
  for (i = 0; i < input_size; 
i++6.03k
)
465
6.03k
    if (inputs[i] >= 0 && inputs[i] < binds->rnum)
466
6.03k
    {
467
6.03k
      ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(binds, inputs[i]);
468
6.03k
      _ccv_nnc_update_bind_destinations_when_free(graph, freed_symbol_d, bind, inputs[i]);
469
6.03k
      const ccv_nnc_tensor_symbol_t alias_to = ccv_nnc_tensor_symbol_alias_to(graph, (ccv_nnc_tensor_symbol_t){
470
6.03k
        .d = inputs[i],
471
6.03k
        .graph = graph
472
6.03k
      });
473
6.03k
      if (alias_to.d >= 0 && 
alias_to.d < binds->rnum0
)
474
0
        _ccv_nnc_update_bind_destinations_when_free(graph, freed_symbol_d, (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(binds, alias_to.d), alias_to.d);
475
6.03k
    }
476
3.02k
  // Note that this works because there is no overlap of inputs / outputs. (What about alias?).
477
6.04k
  for (i = 0; i < output_size; 
i++3.02k
)
478
3.02k
    if (outputs[i] >= 0 && outputs[i] < binds->rnum)
479
3.02k
    {
480
3.02k
      ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(binds, outputs[i]);
481
3.02k
      _ccv_nnc_update_bind_sources_when_free(graph, freed_symbol_d, bind, outputs[i]);
482
3.02k
      const ccv_nnc_tensor_symbol_t alias_to = ccv_nnc_tensor_symbol_alias_to(graph, (ccv_nnc_tensor_symbol_t){
483
3.02k
        .d = outputs[i],
484
3.02k
        .graph = graph
485
3.02k
      });
486
3.02k
      if (alias_to.d >= 0 && 
alias_to.d < binds->rnum0
)
487
0
        _ccv_nnc_update_bind_sources_when_free(graph, freed_symbol_d, (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(binds, alias_to.d), alias_to.d);
488
3.02k
    }
489
3.02k
}
490
491
void ccv_nnc_tensor_variable_free(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_variable_t tensor_variable)
492
6.02k
{
493
6.02k
  // If it contains a symbol, this tensor variable is not a free variable. It is either used as input or output.
494
6.02k
  if (tensor_variable->symbol.d != CCV_NNC_NO_TENSOR_SYMBOL)
495
6.02k
  {
496
6.02k
    // If it is not a free variable, when can we free the symbol and the underlying variable?
497
6.02k
    // 1. There should be no sources (the command generate this tensor should be freed);
498
6.02k
    // 2. The destinations (the commands that uses this tensor) should have no other inputs, or the other inputs has no binded sources as well.
499
6.02k
    ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, tensor_variable->symbol.d);
500
6.02k
    // There should be no source associated with it no more.
501
6.02k
    int free_symbol = 0;
502
6.02k
    if (!bind->sources || 
bind->sources->rnum == 03.02k
)
503
5.01k
    {
504
5.01k
      int i, j;
505
5.01k
      free_symbol = 1; // Assume we can free this symbol.
506
5.01k
      if (!graph->ws)
507
2
        graph->ws = ccv_array_new(sizeof(int), bind->destinations ? bind->destinations->rnum : 
00
, 0);
508
5.01k
      ccv_array_t* const ws = graph->ws;
509
5.01k
      ccv_array_clear(ws);
510
5.01k
      if (bind->destinations)
511
8.03k
        
for (i = 0; 4.01k
i < bind->destinations->rnum;
i++4.02k
)
512
4.02k
          ccv_array_add_unique_int(ws, *(int*)ccv_array_get(bind->destinations, i));
513
5.01k
      const int ws_init_size = ws->rnum;
514
5.01k
      // Go through all the exec symbols use this tensor, to see whether they have inputs that has other sources.
515
5.01k
      if (bind->destinations)
516
8.03k
        
for (i = 0; 4.01k
i < bind->destinations->rnum;)
517
4.02k
        {
518
4.02k
          const int symbol_d = *(int*)ccv_array_get(bind->destinations, i);
519
4.02k
          const ccv_nnc_graph_exec_symbol_t symbol = {
520
4.02k
            .d = symbol_d,
521
4.02k
            .graph = graph->tape
522
4.02k
          };
523
4.02k
          const int* inputs; int input_size;
524
4.02k
          ccv_nnc_graph_exec_symbol_io(graph->tape, symbol, &inputs, &input_size, 0, 0);
525
4.02k
          int flag = 0;
526
12.0k
          for (j = 0; !flag && 
j < input_size10.0k
;
j++8.03k
)
527
8.03k
            if (inputs[j] >= 0 && inputs[j] < graph->binds->rnum && inputs[j] != tensor_variable->symbol.d)
528
5.02k
            {
529
5.02k
              ccv_nnc_tensor_variable_graph_bind_t* const other_bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, inputs[j]);
530
5.02k
              flag = (other_bind->index >= 0 && 
other_bind->type != CCV_NNC_TENSOR_CONSTANT2.01k
) ||
(3.01k
other_bind->sources3.01k
&&
other_bind->sources->rnum > 06
);
531
5.02k
            }
532
4.02k
          free_symbol = (free_symbol && 
!flag4.01k
);
533
4.02k
          if (flag)
534
2.00k
            ++i;
535
2.01k
          else {
536
2.01k
            // It is safe to remove this exec. Have to remove it proactively otherwise I may mess up the iteration.
537
2.01k
            if (i < bind->destinations->rnum - 1)
538
4
              *(int*)ccv_array_get(bind->destinations, i) = *(int*)ccv_array_get(bind->destinations, bind->destinations->rnum - 1);
539
2.01k
            --bind->destinations->rnum;
540
2.01k
            // Go over inputs and remove all references from binded destinations.
541
2.01k
            // and go over outputs remove all references from binded sources.
542
2.01k
            const int* outputs; int output_size;
543
2.01k
            ccv_nnc_graph_exec_symbol_io(graph->tape, symbol, 0, 0, &outputs, &output_size);
544
2.01k
            _ccv_nnc_update_bind_sources_destinations_when_free(graph->tape, symbol_d, graph->binds, inputs, input_size, outputs, output_size);
545
2.01k
            const int* outgoings; int outgoing_size;
546
2.01k
            ccv_nnc_graph_exec_symbol_to(graph->tape, symbol, &outgoings, &outgoing_size);
547
3.02k
            for (j = 0; j < outgoing_size; 
j++1.00k
)
548
1.00k
              ccv_array_add_unique_int(ws, outgoings[j]);
549
2.01k
            ccv_nnc_graph_exec_symbol_free(graph->tape, symbol);
550
2.01k
          }
551
4.02k
        }
552
5.01k
      if (free_symbol)
553
3.01k
      {
554
3.01k
        // Nothing here requires this symbol, should be able to free it.
555
3.01k
        _ccv_nnc_tensor_variable_graph_bind_free((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, tensor_variable->symbol.d), 1);
556
3.01k
        ccv_nnc_tensor_symbol_free(graph->tape, tensor_variable->symbol);
557
3.01k
        // Now, go over the outgoings, if it is removed, add more to it. Note that the ws array can grow while iterating over.
558
5.01k
        for (i = ws_init_size; i < ws->rnum; 
i++2.00k
)
559
2.00k
        {
560
2.00k
          const int symbol_d = *(int*)ccv_array_get(ws, i);
561
2.00k
          const ccv_nnc_graph_exec_symbol_t symbol = {
562
2.00k
            .d = symbol_d,
563
2.00k
            .graph = graph->tape
564
2.00k
          };
565
2.00k
          const int* inputs; int input_size;
566
2.00k
          ccv_nnc_graph_exec_symbol_io(graph->tape, symbol, &inputs, &input_size, 0, 0);
567
2.00k
          int flag = 0;
568
5.00k
          for (j = 0; !flag && 
j < input_size4.00k
;
j++3.00k
)
569
3.00k
            if (inputs[j] >= 0 && inputs[j] < graph->binds->rnum)
570
3.00k
            {
571
3.00k
              ccv_nnc_tensor_variable_graph_bind_t* const other_bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(graph->binds, inputs[j]);
572
3.00k
              flag = (other_bind->index >= 0 && 
other_bind->type != CCV_NNC_TENSOR_CONSTANT1.00k
) ||
(2.00k
other_bind->sources2.00k
&&
other_bind->sources->rnum > 02.00k
);
573
3.00k
            }
574
2.00k
          // Went over all the inputs, it turns out no more inputs has other references, safe to remove.
575
2.00k
          if (!flag)
576
1.00k
          {
577
1.00k
            const int* outputs; int output_size;
578
1.00k
            ccv_nnc_graph_exec_symbol_io(graph->tape, symbol, 0, 0, &outputs, &output_size);
579
1.00k
            _ccv_nnc_update_bind_sources_destinations_when_free(graph->tape, symbol_d, graph->binds, inputs, input_size, outputs, output_size);
580
1.00k
            const int* outgoings; int outgoing_size;
581
1.00k
            ccv_nnc_graph_exec_symbol_to(graph->tape, symbol, &outgoings, &outgoing_size);
582
1.00k
            // It it has outgoings, add that for further inspection.
583
2.00k
            for (j = 0; j < outgoing_size; 
j++1.00k
)
584
1.00k
              ccv_array_add_unique_int(ws, outgoings[j]);
585
1.00k
            ccv_nnc_graph_exec_symbol_free(graph->tape, symbol);
586
1.00k
          }
587
2.00k
        }
588
3.01k
      }
589
5.01k
    }
590
6.02k
    // If this symbol is not freed, move the tensor view to the bind.
591
6.02k
    if (!free_symbol)
592
3.01k
    {
593
3.01k
      bind->index = CCV_NNC_TENSOR_NO_VARIABLE_BUT_USED; // This tensor variable will be freed, but this symbol extra will continue exists.
594
3.01k
      bind->tensor_view = tensor_variable->tensor_view; // Transfer the ownership to the bind.
595
3.01k
      tensor_variable->tensor_view = 0;
596
3.01k
    }
597
6.02k
  }
598
6.02k
  _ccv_nnc_tensor_variable_free(graph, tensor_variable, 1);
599
6.02k
}
600
601
void ccv_nnc_dynamic_graph_dot(const ccv_nnc_dynamic_graph_t* const graph, const int flags, FILE* out)
602
11
{
603
11
  ccv_nnc_symbolic_graph_dot(graph->tape, flags, out);
604
11
}