Coverage Report

Created: 2025-02-24 17:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}