/home/liu/actions-runner/_work/ccv/ccv/lib/nnc/ccv_nnc_symbolic_graph_chain_decomposition.c
Line | Count | Source |
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 | | // Implement the new method for exec_dep. We use chain decomposition such that each node only needs to log which chain and at which node to be dependent on. |
8 | | ccv_nnc_exec_dep_t ccv_nnc_exec_dep_new(const ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_graph_visit_t* const visit) |
9 | 0 | { |
10 | 0 | const int exec_symbol_info_size = graph->exec_symbol_info->rnum; |
11 | 0 | int* chain_ids = ccmalloc(sizeof(int) * exec_symbol_info_size * 2); |
12 | 0 | int* chain_pos = chain_ids + exec_symbol_info_size; |
13 | 0 | int* buf = (int*)ccmalloc(sizeof(int) * exec_symbol_info_size); |
14 | 0 | int* reversed_depth = buf; |
15 | 0 | 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); |
16 | 0 | int i, j; |
17 | | // Go reverse order to generate the distance from sink. |
18 | 0 | ccv_nnc_graph_visit_for_reversed(visit, exec_symbol_info, node, idx, term) { |
19 | 0 | if (node->flags & CCV_NNC_GRAPH_EXEC_DEAD) |
20 | 0 | continue; |
21 | 0 | chain_ids[idx] = -1; |
22 | 0 | if (!node->outgoings || node->outgoings->rnum == 0) |
23 | 0 | { |
24 | 0 | reversed_depth[idx] = 0; |
25 | 0 | continue; |
26 | 0 | } |
27 | 0 | const int outgoing = *(int*)ccv_array_get(node->outgoings, 0); |
28 | 0 | int depth = reversed_depth[outgoing]; |
29 | 0 | for (i = 1; i < node->outgoings->rnum; i++) |
30 | 0 | { |
31 | 0 | const int outgoing = *(int*)ccv_array_get(node->outgoings, i); |
32 | 0 | depth = ccv_max(depth, reversed_depth[outgoing]); |
33 | 0 | } |
34 | 0 | reversed_depth[idx] = depth + 1; |
35 | 0 | } ccv_nnc_graph_visit_endfor |
36 | | // Go in order to generate chain ids (if there are multiple exits, we use the reverse depth to break the tie). |
37 | | // Note that we cannot use depth so-far because then multiple exit nodes are equally good to "inherit" the chain selection. |
38 | 0 | int chain_count = 0; |
39 | 0 | ccv_nnc_graph_visit_for(visit, exec_symbol_info, node, idx, term) { |
40 | 0 | if (node->flags & CCV_NNC_GRAPH_EXEC_DEAD) |
41 | 0 | continue; |
42 | 0 | int chain_id = chain_ids[idx]; |
43 | 0 | if (chain_ids[idx] < 0) |
44 | 0 | { |
45 | 0 | chain_id = chain_count; |
46 | 0 | chain_ids[idx] = chain_id; |
47 | 0 | chain_pos[idx] = 1; // The first one in this chain. 1-based index because in sparse matrix, 0 is the default value. |
48 | 0 | chain_count += 1; |
49 | 0 | } |
50 | 0 | if (!node->outgoings || node->outgoings->rnum == 0) |
51 | 0 | continue; |
52 | 0 | int depth = -1; |
53 | 0 | int next_idx = -1; |
54 | 0 | for (i = 0; i < node->outgoings->rnum; i++) |
55 | 0 | { |
56 | 0 | const int outgoing = *(int*)ccv_array_get(node->outgoings, i); |
57 | 0 | if (chain_ids[outgoing] < 0 && reversed_depth[outgoing] > depth) |
58 | 0 | depth = reversed_depth[outgoing], next_idx = outgoing; |
59 | 0 | } |
60 | 0 | if (next_idx >= 0) |
61 | 0 | { |
62 | 0 | chain_ids[next_idx] = chain_id; |
63 | 0 | assert(reversed_depth[idx] - depth >= 1); |
64 | 0 | chain_pos[next_idx] = chain_pos[idx] + (reversed_depth[idx] - depth); |
65 | 0 | } |
66 | 0 | } ccv_nnc_graph_visit_endfor |
67 | 0 | if (exec_symbol_info_size < chain_count * 3) // Be more conservative on RAM usage. |
68 | 0 | buf = ccrealloc(buf, sizeof(int) * chain_count * 3); |
69 | 0 | ccv_sparse_matrix_t* deps = ccv_sparse_matrix_new(graph->exec_symbol_info->rnum, chain_count, CCV_32S | CCV_C2, CCV_SPARSE_ROW_MAJOR, 0); |
70 | | // It logs which pos on that chain we depend on. We can simply compare that with the chain_pos for a node to know if they are ancestors. |
71 | 0 | #define for_block(x, val) \ |
72 | 0 | do { \ |
73 | 0 | if (((int32_t*)val)[0] > 0) \ |
74 | 0 | { \ |
75 | 0 | buf[buf_size * 3] = x; \ |
76 | 0 | buf[buf_size * 3 + 1] = ((int32_t*)val)[0]; \ |
77 | 0 | buf[buf_size * 3 + 2] = ((int32_t*)val)[1] + 1; \ |
78 | 0 | ++buf_size; \ |
79 | 0 | } \ |
80 | 0 | } while (0) |
81 | 0 | int buf_size; |
82 | 0 | ccv_nnc_graph_visit_for(visit, exec_symbol_info, node, idx, term) { |
83 | 0 | if (node->flags & CCV_NNC_GRAPH_EXEC_DEAD) |
84 | 0 | continue; |
85 | 0 | buf_size = 0; /* save all its parent deps to this buffer */ |
86 | 0 | ccv_sparse_matrix_vector_t* vector = ccv_get_sparse_matrix_vector(deps, idx); |
87 | 0 | if (vector) |
88 | 0 | CCV_SPARSE_VECTOR_FOREACH(deps, vector, for_block); |
89 | 0 | if (!node->outgoings) |
90 | 0 | continue; |
91 | 0 | const int chain_id = chain_ids[idx]; |
92 | 0 | const int pos = chain_pos[idx]; |
93 | 0 | for (i = 0; i < node->outgoings->rnum; i++) |
94 | 0 | { |
95 | 0 | const int outgoing = *(int*)ccv_array_get(node->outgoings, i); |
96 | 0 | const int outgoing_chain_id = chain_ids[outgoing]; |
97 | 0 | if (outgoing_chain_id != chain_id) |
98 | 0 | { |
99 | 0 | ccv_numeric_data_t cell = ccv_get_sparse_matrix_cell(deps, outgoing, chain_id); |
100 | | /* If not found, set, if the current node is the destination node, no need |
101 | | * set itself as parent of subsequent nodes because its terminal nature. */ |
102 | 0 | if (!cell.i32 || cell.i32[0] == 0 || cell.i32[0] < pos) |
103 | 0 | { |
104 | 0 | int p[2] = { pos, 1 }; |
105 | 0 | ccv_set_sparse_matrix_cell(deps, outgoing, chain_id, &p); |
106 | 0 | } |
107 | 0 | } |
108 | 0 | if (buf_size > 0) |
109 | 0 | { |
110 | 0 | ccv_sparse_matrix_vector_t* vector = ccv_get_sparse_matrix_vector(deps, outgoing); |
111 | 0 | for (j = 0; j < buf_size; j++) /* set with all idx's dependencies as well */ |
112 | 0 | { |
113 | 0 | if (outgoing_chain_id == buf[j * 3]) // We don't need to add as dependency for the same chain. |
114 | 0 | continue; |
115 | 0 | if (!vector) |
116 | 0 | { |
117 | 0 | ccv_set_sparse_matrix_cell(deps, outgoing, buf[j * 3], &buf[j * 3 + 1]); |
118 | 0 | vector = ccv_get_sparse_matrix_vector(deps, outgoing); |
119 | 0 | continue; |
120 | 0 | } |
121 | 0 | ccv_numeric_data_t cell = ccv_get_sparse_matrix_cell_from_vector(deps, vector, buf[j * 3]); |
122 | | /* If not found, set. Otherwise, set to the latest one only if it is later. */ |
123 | 0 | if (!cell.i32) |
124 | 0 | ccv_set_sparse_matrix_cell_from_vector(deps, vector, buf[j * 3], &buf[j * 3 + 1]); |
125 | 0 | else if (cell.i32[0] == 0 || cell.i32[0] < buf[j * 3 + 1]) |
126 | 0 | ccv_set_sparse_matrix_cell_from_vector(deps, vector, buf[j * 3], &buf[j * 3 + 1]); |
127 | 0 | else if (cell.i32[0] == buf[j * 3 + 1]) { // If we point to the same one, use the longest. |
128 | 0 | int p[2] = { cell.i32[0], ccv_max(buf[j * 3 + 2], cell.i32[1]) }; |
129 | 0 | ccv_set_sparse_matrix_cell_from_vector(deps, vector, buf[j * 3], &p); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | 0 | } ccv_nnc_graph_visit_endfor |
135 | 0 | #undef for_block |
136 | 0 | ccfree(buf); |
137 | 0 | ccv_nnc_exec_dep_t exec_dep = { |
138 | 0 | .chain_ids = chain_ids, |
139 | 0 | .chain_pos = chain_pos, |
140 | 0 | .deps = deps |
141 | 0 | }; |
142 | 0 | return exec_dep; |
143 | 0 | } |
144 | | |
145 | | void ccv_nnc_exec_dep_free(const ccv_nnc_exec_dep_t exec_dep) |
146 | 2 | { |
147 | 2 | ccfree(exec_dep.chain_ids); |
148 | 2 | ccv_matrix_free(exec_dep.deps); |
149 | 2 | } |