## Coverage Report

#### Created: 2026-04-20 13:39

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
    }