Coverage Report

Created: 2019-07-03 22:50

/home/liu/buildslave/linux-x64-runtests/build/lib/nnc/ccv_nnc_dynamic_graph_minimize.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.5 API
9
10
void ccv_nnc_dynamic_graph_minimize(ccv_nnc_dynamic_graph_t* const dynamic_graph, const ccv_nnc_cmd_t minimizer, const ccv_nnc_tensor_variable_t* const losses, const int loss_size, const ccv_nnc_tensor_variable_t* const dlosses_optional, ccv_nnc_tensor_variable_t* const parameters, const int parameter_size, ccv_nnc_tensor_variable_t* const saved_aux)
11
1.00k
{
12
1.00k
  assert(parameter_size > 0);
13
1.00k
  assert(loss_size > 0);
14
1.00k
  int d, i, j, k;
15
1.00k
  int losses_source_size = 0;
16
1.00k
  // Both f_variable and tensor_variable should be, at least, executed. Otherwise we cannot differentiate.
17
2.00k
  for (i = 0; i < loss_size; 
i++1.00k
)
18
1.00k
  {
19
1.00k
    assert(losses[i]->symbol.d >= 0);
20
1.00k
    const ccv_nnc_tensor_variable_graph_bind_t* const loss_symbol_extra = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, losses[i]->symbol.d);
21
1.00k
    assert(loss_symbol_extra->sources && loss_symbol_extra->sources->rnum > 0);
22
1.00k
    losses_source_size += loss_symbol_extra->sources->rnum;
23
1.00k
  }
24
2.00k
  
for (i = 0; 1.00k
i < parameter_size;
i++1.00k
)
25
1.00k
  {
26
1.00k
    assert(parameters[i]->symbol.d >= 0);
27
1.00k
    assert(((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, parameters[i]->symbol.d))->destinations &&
28
1.00k
      ((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, parameters[i]->symbol.d))->destinations->rnum > 0);
29
1.00k
  }
30
1.00k
  const int exec_symbol_info_size = ccv_nnc_graph_exec_symbol_count(dynamic_graph->tape);
31
1.00k
  ccv_array_t* const sources = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 1, 0);
32
1.00k
  if (!dynamic_graph->ws)
33
1
    dynamic_graph->ws = ccv_array_new(sizeof(int), exec_symbol_info_size * 2 + ((exec_symbol_info_size + 31) >> 5), 0);
34
1.00k
  ccv_array_t* const ws = dynamic_graph->ws;
35
1.00k
  ccv_array_resize(ws, exec_symbol_info_size * 2 + ((exec_symbol_info_size + 31) >> 5));
36
1.00k
  // set visited to all 0.
37
1.00k
  memset((uint32_t*)ccv_array_get(ws, exec_symbol_info_size * 2), 0, sizeof(uint32_t) * ((exec_symbol_info_size + 31) >> 5));
38
2.00k
  for (i = 0; i < parameter_size; 
i++1.00k
)
39
1.00k
  {
40
1.00k
    ccv_array_t* const destinations = ((ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, parameters[i]->symbol.d))->destinations;
41
2.00k
    for (j = 0; j < destinations->rnum; 
j++1.00k
)
42
1.00k
      ccv_nnc_insert_if_prior_to_any(dynamic_graph->tape,
43
1.00k
        *(int*)ccv_array_get(destinations, j),
44
1.00k
        sources, (uint32_t*)ccv_array_get(ws, exec_symbol_info_size * 2),
45
1.00k
        (int*)ccv_array_get(ws, 0), (int*)ccv_array_get(ws, exec_symbol_info_size));
46
1.00k
  }
47
1.00k
  ccv_array_t* const destinations = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), losses_source_size, 0);
48
2.00k
  for (i = 0; i < loss_size; 
i++1.00k
)
49
1.00k
  {
50
1.00k
    const ccv_nnc_tensor_variable_graph_bind_t* const loss_symbol_extra = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, losses[i]->symbol.d);
51
2.00k
    for (j = 0; j < loss_symbol_extra->sources->rnum; 
j++1.00k
)
52
1.00k
    {
53
1.00k
      const int symbol_d = *(int*)ccv_array_get(loss_symbol_extra->sources, j);
54
1.00k
      int flag = 0;
55
1.00k
      for (k = 0; !flag && k < destinations->rnum; 
k++0
)
56
0
        flag = (symbol_d == ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(destinations, k))->d);
57
1.00k
      if (!flag)
58
1.00k
      {
59
1.00k
        const ccv_nnc_graph_exec_symbol_t symbol = {
60
1.00k
          .d = symbol_d,
61
1.00k
          .graph = dynamic_graph->tape
62
1.00k
        };
63
1.00k
        ccv_array_push(destinations, &symbol);
64
1.00k
      }
65
1.00k
    }
66
1.00k
  }
67
1.00k
  // Go over sources, because destinations will get removed all the time, thus, the index is not accurate.
68
1.00k
  if (destinations->rnum > 1)
69
0
    for (i = 0 ; i < destinations->rnum; i++)
70
0
    {
71
0
      memset((uint32_t*)ccv_array_get(ws, exec_symbol_info_size * 2), 0, sizeof(uint32_t) * ((exec_symbol_info_size + 31) >> 5));
72
0
      ccv_nnc_remove_if_prior_to_any(dynamic_graph->tape,
73
0
        ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(destinations, i))->d,
74
0
        destinations, (uint32_t*)ccv_array_get(ws, exec_symbol_info_size * 2),
75
0
        (int*)ccv_array_get(ws, 0), (int*)ccv_array_get(ws, exec_symbol_info_size));
76
0
    }
77
1.00k
  ccv_nnc_tensor_symbol_t loss_symbols[loss_size];
78
2.00k
  for (i = 0; i < loss_size; 
i++1.00k
)
79
1.00k
    loss_symbols[i] = losses[i]->symbol;
80
1.00k
  ccv_nnc_tensor_symbol_t parameter_symbols[parameter_size];
81
2.00k
  for (i = 0; i < parameter_size; 
i++1.00k
)
82
1.00k
    parameter_symbols[i] = parameters[i]->symbol;
83
1.00k
  ccv_array_t* const symbol_stack = ccv_array_new(sizeof(ccv_nnc_tape_symbol_t), 1, 0);
84
1.00k
  ccv_nnc_tensor_symbol_new_hook(dynamic_graph->tape, ccv_nnc_dynamic_graph_push_backward_tensor_symbol, symbol_stack);
85
1.00k
  ccv_nnc_tensor_symbol_alias_new_hook(dynamic_graph->tape, ccv_nnc_dynamic_graph_push_backward_tensor_symbol_alias, symbol_stack);
86
1.00k
  ccv_nnc_graph_exec_symbol_new_hook(dynamic_graph->tape, ccv_nnc_dynamic_graph_push_backward_graph_exec_symbol, symbol_stack);
87
1.00k
  ccv_nnc_tensor_symbol_t updated_parameter_symbols[parameter_size];
88
1.00k
  const int saved_aux_size = parameter_size * ccv_nnc_minimizer_saved_aux_size(minimizer);
89
1.00k
  ccv_nnc_tensor_symbol_map_t saved_aux_symbols[saved_aux_size];
90
1.00k
  ccv_nnc_graph_exec_symbol_t update_exec_symbols[parameter_size];
91
1.00k
  ccv_nnc_symbolic_graph_minimize(dynamic_graph->tape, minimizer,
92
1.00k
    loss_symbols, loss_size, parameter_symbols, parameter_size, 0, 0,
93
1.00k
    (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(sources, 0), sources->rnum,
94
1.00k
    (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(destinations, 0), destinations->rnum,
95
1.00k
    0, updated_parameter_symbols, saved_aux_symbols, update_exec_symbols);
96
1.00k
  ccv_nnc_tensor_symbol_new_hook(dynamic_graph->tape, 0, 0);
97
1.00k
  ccv_nnc_tensor_symbol_alias_new_hook(dynamic_graph->tape, 0, 0);
98
1.00k
  ccv_nnc_graph_exec_symbol_new_hook(dynamic_graph->tape, 0, 0);
99
1.00k
  // Bind generated tensors.
100
1.00k
  ccv_array_t* const tensor_binds = ccv_array_new(sizeof(ccv_nnc_tensor_bind_t), dynamic_graph->vars->rnum + 2, 0);
101
8.00k
  for (i = 0; i < dynamic_graph->vars->rnum; 
i++7.00k
)
102
7.00k
  {
103
7.00k
    ccv_nnc_tensor_variable_t var = *(ccv_nnc_tensor_variable_t*)ccv_array_get(dynamic_graph->vars, i);
104
7.00k
    if (var && var->tensor_view && var->symbol.d >= 0)
105
6.00k
    {
106
6.00k
      ccv_nnc_tensor_bind_t bind = {
107
6.00k
        .symbol = var->symbol,
108
6.00k
        .tensor = (ccv_nnc_tensor_t*)CCV_NNC_TENSOR_VIEW(var->tensor_view)
109
6.00k
      };
110
6.00k
      ccv_array_push(tensor_binds, &bind);
111
6.00k
    }
112
7.00k
  }
113
7.00k
  for (i = 0; i < dynamic_graph->binds->rnum; 
i++6.00k
)
114
6.00k
  {
115
6.00k
    ccv_nnc_tensor_variable_graph_bind_t* const bind = (ccv_nnc_tensor_variable_graph_bind_t*)ccv_array_get(dynamic_graph->binds, i);
116
6.00k
    if (bind->index == CCV_NNC_TENSOR_NO_VARIABLE_BUT_USED && 
bind->tensor_view0
)
117
0
    {
118
0
      ccv_nnc_tensor_bind_t b = {
119
0
        .symbol = {
120
0
          .d = i,
121
0
          .graph = dynamic_graph->tape,
122
0
        },
123
0
        .tensor = (ccv_nnc_tensor_t*)CCV_NNC_TENSOR_VIEW(bind->tensor_view)
124
0
      };
125
0
      ccv_array_push(tensor_binds, &b);
126
0
    }
127
6.00k
  }
128
1.00k
  // Compiled graph comes from the dloss.
129
1.00k
  ccv_array_clear(sources);
130
1.00k
  ccv_nnc_tensor_symbol_t dloss_symbols[loss_size];
131
2.00k
  for (i = 0; i < loss_size; 
i++1.00k
)
132
1.00k
  {
133
1.00k
    dloss_symbols[i] = ccv_nnc_tensor_symbol_for_backward(dynamic_graph->tape, losses[i]->symbol);
134
1.00k
    assert(dloss_symbols[i].d >= 0);
135
1.00k
  }
136
2.00k
  
for (d = 0; 1.00k
d < destinations->rnum;
d++1.00k
)
137
1.00k
  {
138
1.00k
    const ccv_nnc_graph_exec_symbol_t* const destination = (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(destinations, d);
139
1.00k
    const int* outgoings; int outgoing_size;
140
1.00k
    ccv_nnc_graph_exec_symbol_to(dynamic_graph->tape, *destination, &outgoings, &outgoing_size);
141
2.00k
    for (i = 0; i < outgoing_size; 
i++1.00k
)
142
1.00k
    {
143
1.00k
      const int exec_idx = outgoings[i];
144
1.00k
      const int* inputs; int input_size;
145
1.00k
      ccv_nnc_graph_exec_symbol_io(dynamic_graph->tape, (ccv_nnc_graph_exec_symbol_t){
146
1.00k
        .d = exec_idx,
147
1.00k
        .graph = dynamic_graph->tape
148
1.00k
      }, &inputs, &input_size, 0, 0);
149
1.00k
      for (j = 0; j < input_size; 
j++0
)
150
1.00k
      {
151
1.00k
        const int input = inputs[j];
152
1.00k
        const int alias_ref = input >= 0 ? ccv_nnc_tensor_symbol_alias_to(dynamic_graph->tape, (ccv_nnc_tensor_symbol_t){
153
1.00k
          .d = input,
154
1.00k
          .graph = dynamic_graph->tape
155
1.00k
        }).d : 
CCV_NNC_NO_TENSOR_SYMBOL0
; // This could be CCV_NNC_NO_TENSOR_SYMBOL, which is negative.
156
1.00k
        // alias_ref is either exists, or -1.
157
1.00k
        int flag = 0;
158
2.00k
        for (k = 0; !flag && 
k < loss_size1.00k
;
k++1.00k
)
159
1.00k
          flag = (dloss_symbols[k].d == input || 
dloss_symbols[k].d == alias_ref0
);
160
1.00k
        if (flag)
161
1.00k
        {
162
1.00k
          flag = 0;
163
1.00k
          for (k = 0; !flag && k < sources->rnum; 
k++0
)
164
0
            flag = (exec_idx == ((ccv_nnc_graph_exec_symbol_t*)ccv_array_get(sources, k))->d);
165
1.00k
          if (!flag)
166
1.00k
          {
167
1.00k
            const ccv_nnc_graph_exec_symbol_t source = {
168
1.00k
              .d = exec_idx,
169
1.00k
              .graph = dynamic_graph->tape
170
1.00k
            };
171
1.00k
            ccv_array_push(sources, &source);
172
1.00k
          }
173
1.00k
          break;
174
1.00k
        }
175
1.00k
      }
176
1.00k
    }
177
1.00k
  }
178
1.00k
  ccv_array_free(destinations);
179
1.00k
  int freeable_size = 0;
180
1.00k
  ccv_nnc_tensor_variable_t freeables[parameter_size + saved_aux_size];
181
1.00k
  // Bind dt tensor.
182
2.00k
  for (i = 0; i < parameter_size; 
i++1.00k
)
183
1.00k
  {
184
1.00k
    const ccv_nnc_tensor_symbol_t symbol = updated_parameter_symbols[i];
185
1.00k
    if (parameters[i]->symbol.d >= 0)
186
1.00k
      freeables[freeable_size++] = ccv_nnc_tensor_variable_exchange_new(dynamic_graph, parameters[i]);
187
1.00k
    ccv_nnc_tensor_t* tensor = ccv_nnc_tensor_from_variable(dynamic_graph, parameters[i]);
188
1.00k
    const ccv_nnc_tensor_bind_t dt_bind = {
189
1.00k
      .symbol = symbol,
190
1.00k
      .tensor = tensor
191
1.00k
    };
192
1.00k
    ccv_array_push(tensor_binds, &dt_bind);
193
1.00k
  }
194
2.00k
  for (i = 0; i < saved_aux_size; 
i++1.00k
)
195
1.00k
  {
196
1.00k
    const ccv_nnc_tensor_symbol_map_t symbol_map = saved_aux_symbols[i];
197
1.00k
    if (saved_aux[i]->symbol.d >= 0)
198
0
      freeables[freeable_size++] = ccv_nnc_tensor_variable_exchange_new(dynamic_graph, saved_aux[i]);
199
1.00k
    ccv_nnc_tensor_t* tensor = ccv_nnc_tensor_from_variable(dynamic_graph, saved_aux[i]);
200
1.00k
    ccv_nnc_tensor_bind_t aux_bind = {
201
1.00k
      .symbol = symbol_map.source,
202
1.00k
      .tensor = tensor
203
1.00k
    };
204
1.00k
    ccv_array_push(tensor_binds, &aux_bind);
205
1.00k
    aux_bind.symbol = symbol_map.destination;
206
1.00k
    ccv_array_push(tensor_binds, &aux_bind);
207
1.00k
  }
208
1.00k
  ccv_nnc_graph_t* graph = 0;
209
1.00k
  ccv_nnc_tensor_arena_t* tensor_arena = 0;
210
1.00k
  ccv_nnc_graph_exec_arena_t* exec_arena = 0;
211
1.00k
  if (dlosses_optional)
212
0
  {
213
0
    // If provided df variable, no need to set to all ones.
214
0
    for (i = 0; i < loss_size; i++)
215
0
    {
216
0
      const ccv_nnc_tensor_bind_t df_bind = {
217
0
        .symbol = dloss_symbols[i],
218
0
        .tensor = ccv_nnc_tensor_from_variable(dynamic_graph, dlosses_optional[i])
219
0
      };
220
0
      ccv_array_push(tensor_binds, &df_bind);
221
0
    }
222
0
    ccv_nnc_symbolic_graph_compile(dynamic_graph->tape,
223
0
      (ccv_nnc_tensor_bind_t*)ccv_array_get(tensor_binds, 0), tensor_binds->rnum,
224
0
      0, 0,
225
0
      (ccv_nnc_graph_exec_symbol_t*)ccv_array_get(sources, 0), sources->rnum,
226
0
      update_exec_symbols, parameter_size,
227
0
      &graph, &tensor_arena, &exec_arena);
228
0
    ccv_array_free(sources);
229
1.00k
  } else {
230
1.00k
    ccv_nnc_graph_exec_symbol_t set_ones = ccv_nnc_graph_exec_symbol_new(dynamic_graph->tape, CMD_SET_FORWARD(1), 0, 0, dloss_symbols, loss_size, 0);
231
2.00k
    for (i = 0; i < sources->rnum; 
i++1.00k
)
232
1.00k
      ccv_nnc_graph_exec_symbol_concat(dynamic_graph->tape, set_ones, *(ccv_nnc_graph_exec_symbol_t*)ccv_array_get(sources, i));
233
1.00k
    ccv_array_free(sources);
234
1.00k
    ccv_nnc_symbolic_graph_compile(dynamic_graph->tape,
235
1.00k
      (ccv_nnc_tensor_bind_t*)ccv_array_get(tensor_binds, 0), tensor_binds->rnum,
236
1.00k
      0, 0,
237
1.00k
      &set_ones, 1,
238
1.00k
      update_exec_symbols, parameter_size,
239
1.00k
      &graph, &tensor_arena, &exec_arena);
240
1.00k
    ccv_nnc_graph_exec_symbol_free(dynamic_graph->tape, set_ones);
241
1.00k
  }
242
1.00k
  ccv_array_free(tensor_binds);
243
1.00k
  ccv_nnc_graph_run(graph, 0, 0, 0, TRAVERSE_FULL);
244
1.00k
  ccv_nnc_graph_free(graph);
245
1.00k
  ccv_nnc_tensor_arena_free(tensor_arena);
246
1.00k
  ccv_nnc_graph_exec_arena_free(exec_arena);
247
1.00k
  // Remove newly added symbols to restore the graph.
248
16.0k
  for (i = 0; i < symbol_stack->rnum; 
i++15.0k
)
249
15.0k
  {
250
15.0k
    const ccv_nnc_tape_symbol_t* const symbol = (ccv_nnc_tape_symbol_t*)ccv_array_get(symbol_stack, i);
251
15.0k
    if (symbol->type == CCV_NNC_SYMBOL_TENSOR || 
symbol->type == CCV_NNC_SYMBOL_TENSOR_ALIAS5.00k
)
252
10.0k
      ccv_nnc_tensor_symbol_free(dynamic_graph->tape, (ccv_nnc_tensor_symbol_t){
253
10.0k
        .d = symbol->d,
254
10.0k
        .graph = dynamic_graph->tape
255
10.0k
      });
256
5.00k
    else if (symbol->type == CCV_NNC_SYMBOL_GRAPH_EXEC)
257
5.00k
      ccv_nnc_graph_exec_symbol_free(dynamic_graph->tape, (ccv_nnc_graph_exec_symbol_t){
258
5.00k
        .d = symbol->d,
259
5.00k
        .graph = dynamic_graph->tape
260
5.00k
      });
261
15.0k
  }
262
1.00k
  // Now, able to free some of the reused outputs. This need to be the last step otherwise some of the exec symbols
263
1.00k
  // above may be freed by this operation.
264
2.00k
  for (i = 0; i < freeable_size; 
i++1.00k
)
265
1.00k
    ccv_nnc_tensor_variable_free(dynamic_graph, freeables[i]);
266
1.00k
  ccv_array_free(symbol_stack);
267
1.00k
}