## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/liu/actions-runner/_work/ccv/ccv/test/int/nnc/symbolic.graph.vgg.d.tests.c

    Line
    Count
    Source
    1
    #include "case.h"
    2
    #include "ccv_case.h"
    3
    #include "ccv_nnc_case.h"
    4
    #include <ccv.h>
    5
    #include <nnc/ccv_nnc.h>
    6
    #include <nnc/ccv_nnc_easy.h>
    7
    #include <inc/ccv_convnet_internal.h>
    8
    9
    TEST_SETUP()
    10
    {
    11
      ccv_nnc_init();
    12
    }
    13
    14
    static ccv_nnc_symbolic_graph_t* ccv_nnc_simple_symbolic_graph(ccv_convnet_t* convnet, ccv_nnc_tensor_t* input, ccv_nnc_tensor_t* output, ccv_nnc_graph_exec_symbol_t* source_symbol, ccv_nnc_graph_exec_symbol_t* dest_symbol, ccv_nnc_tensor_symbol_t* input_symbol_ref, ccv_nnc_tensor_symbol_t* output_symbol_ref, ccv_nnc_tensor_symbol_t* w_symbols, ccv_nnc_tensor_symbol_t* bias_symbols)
    15
    1
    {
    16
    1
      int i;
    17
      // We only create the graph compute to the last fc layer.
    18
    1
      ccv_nnc_symbolic_graph_t* symbolic_vgg = ccv_nnc_symbolic_graph_new();
    19
    1
      ccv_nnc_tensor_param_t input_info = input->info;
    20
    1
      ccv_nnc_tensor_symbol_t input_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, input->info, 0);
    21
    1
      *input_symbol_ref = input_symbol;
    22
    1
      ccv_nnc_tensor_symbol_t output_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, output->info, 0);
    23
    1
      *output_symbol_ref = output_symbol;
    24
    1
      ccv_nnc_graph_exec_symbol_t previous_exec_symbol;
    25
    22
      for (i = 0; i < convnet->count; i++21)
    26
    21
      {
    27
    21
        ccv_convnet_layer_t* layer = convnet->layers + i;
    28
    21
        int rows, cols, partition;
    29
    21
        ccv_convnet_make_output(layer, layer->input.matrix.rows, layer->input.matrix.cols, &rows, &cols, &partition);
    30
    21
        ccv_nnc_tensor_param_t tensor_info = output->info;
    31
    21
        ccv_nnc_tensor_symbol_t tensor_symbol = output_symbol;
    32
    21
        if (i < convnet->count - 1)
    33
    20
        {
    34
    20
          if (layer->type == CCV_CONVNET_FULL_CONNECT)
    35
    2
            tensor_info = CPU_TENSOR_NHWC(32F, rows * cols * partition);
    36
    18
          else
    37
    18
            tensor_info = CPU_TENSOR_NHWC(32F, rows, cols, (layer->type == CCV_CONVNET_CONVOLUTIONAL ? layer->net.convolutional.count : layer->input.matrix.channels));
    38
    20
          tensor_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, tensor_info, 0);
    39
    20
        }
    40
    21
        ccv_nnc_graph_exec_symbol_t exec_symbol = {0};
    41
    21
        if (layer->type == CCV_CONVNET_CONVOLUTIONAL)
    42
    13
        {
    43
    13
          ccv_nnc_tensor_symbol_t w_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, CPU_TENSOR_NHWC(32F, layer->net.convolutional.count, layer->net.convolutional.rows, layer->net.convolutional.cols, layer->net.convolutional.channels), 0);
    44
    13
          w_symbols[i] = w_symbol;
    45
    13
          ccv_nnc_tensor_symbol_t bias_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, CPU_TENSOR_NHWC(32F, layer->net.convolutional.count), 0);
    46
    13
          bias_symbols[i] = bias_symbol;
    47
    13
          ccv_nnc_cmd_t cmd = CMD_CONVOLUTION_FORWARD(1, layer->net.convolutional.count, layer->net.convolutional.rows, layer->net.convolutional.cols, layer->net.convolutional.channels);
    48
    13
          exec_symbol = ccv_nnc_graph_exec_symbol_new(symbolic_vgg, cmd, TENSOR_SYMBOL_LIST(input_symbol, w_symbol, bias_symbol), TENSOR_SYMBOL_LIST(tensor_symbol), 0);
    49
    13
        } else if (8layer->type == CCV_CONVNET_MAX_POOL8) {
    50
    5
          ccv_nnc_cmd_t cmd = CMD_MAX_POOL_FORWARD(layer->net.pool.size, layer->net.pool.size);
    51
    5
          exec_symbol = ccv_nnc_graph_exec_symbol_new(symbolic_vgg, cmd, TENSOR_SYMBOL_LIST(input_symbol), TENSOR_SYMBOL_LIST(tensor_symbol), 0);
    52
    5
        } else if (3layer->type == CCV_CONVNET_FULL_CONNECT3) {
    53
    3
          ccv_nnc_tensor_symbol_t w_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, CPU_TENSOR_NHWC(32F, layer->net.full_connect.count, layer->input.node.count), 0);
    54
    3
          w_symbols[i] = w_symbol;
    55
    3
          ccv_nnc_tensor_symbol_t bias_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, CPU_TENSOR_NHWC(32F, layer->net.full_connect.count), 0);
    56
    3
          bias_symbols[i] = bias_symbol;
    57
    3
          ccv_nnc_cmd_t cmd = CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1));
    58
          // If the input is not what I expected (array), reshape it.
    59
    3
          if (input_info.dim[0] != ccv_nnc_tensor_count(input_info))
    60
    1
            input_symbol = ccv_nnc_tensor_symbol_alias_new(symbolic_vgg, input_symbol, ccv_nnc_no_ofs, DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, ccv_nnc_tensor_count(input_info)), 0);
    61
    3
          exec_symbol = ccv_nnc_graph_exec_symbol_new(symbolic_vgg, cmd, TENSOR_SYMBOL_LIST(input_symbol, w_symbol, bias_symbol), TENSOR_SYMBOL_LIST(tensor_symbol), 0);
    62
    3
        } else {
    63
    0
          assert("unreachable");
    64
    0
        }
    65
    21
        if (i != 0)
    66
    20
          ccv_nnc_graph_exec_symbol_concat(symbolic_vgg, previous_exec_symbol, exec_symbol);
    67
    21
        previous_exec_symbol = exec_symbol;
    68
    21
        if (i == 0)
    69
    1
          *source_symbol = exec_symbol;
    70
    21
        if (i < convnet->count - 1 &&
    71
    21
          (20layer->type == CCV_CONVNET_CONVOLUTIONAL20 || layer->type == CCV_CONVNET_FULL_CONNECT7))
    72
    15
        {
    73
          // Create the ReLU layer.
    74
    15
          ccv_nnc_cmd_t cmd = CMD_RELU_FORWARD();
    75
    15
          ccv_nnc_tensor_symbol_t next_symbol = ccv_nnc_tensor_symbol_new(symbolic_vgg, tensor_info, 0);
    76
    15
          exec_symbol = ccv_nnc_graph_exec_symbol_new(symbolic_vgg, cmd, TENSOR_SYMBOL_LIST(tensor_symbol), TENSOR_SYMBOL_LIST(next_symbol), 0);
    77
    15
          ccv_nnc_graph_exec_symbol_concat(symbolic_vgg, previous_exec_symbol, exec_symbol);
    78
    15
          tensor_symbol = next_symbol;
    79
    15
          previous_exec_symbol = exec_symbol;
    80
    15
        }
    81
    21
        if (i == convnet->count - 1)
    82
    1
          *dest_symbol = exec_symbol;
    83
        // This is the input of next layer.
    84
    21
        input_symbol = tensor_symbol;
    85
    21
        input_info = tensor_info;
    86
    21
      }
    87
    1
      return symbolic_vgg;
    88
    1
    }
    89
    90
    #ifdef HAVE_LIBPNG
    91
    TEST_CASE("run vgg-d graph from its symbolic representation")
    92
    1
    {
    93
    1
      ccv_convnet_t* convnet = ccv_convnet_read(0, "../../../samples/image-net-2012-vgg-d.sqlite3");
    94
    1
      ccv_dense_matrix_t* image = 0;
    95
    1
      ccv_read("../../../samples/dex.png", &image, CCV_IO_ANY_FILE | CCV_IO_RGB_COLOR);
    96
    1
      ccv_dense_matrix_t* input = 0;
    97
    1
      ccv_convnet_input_formation(convnet->input, image, &input);
    98
    1
      ccv_matrix_free(image);
    99
    1
      ccv_dense_matrix_t* sliced = 0;
    100
    1
      ccv_slice(input, (ccv_matrix_t**)&sliced, 0, (input->rows - 225) / 2, (input->cols - 225) / 2, 225, 225);
    101
    1
      ccv_matrix_free(input);
    102
    1
      ccv_dense_matrix_t* b = 0;
    103
    1
      ccv_convnet_encode(convnet, &sliced, &b, 1);
    104
    1
      ccv_nnc_tensor_t* c = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1000), 0);
    105
    1
      ccv_nnc_tensor_symbol_t* w_symbols = ccmalloc(sizeof(ccv_nnc_tensor_symbol_t) * convnet->count);
    106
    1
      ccv_nnc_tensor_symbol_t* bias_symbols = ccmalloc(sizeof(ccv_nnc_tensor_symbol_t) * convnet->count);
    107
    1
      ccv_nnc_graph_exec_symbol_t source_symbol, dest_symbol;
    108
    1
      ccv_nnc_tensor_symbol_t input_symbol, output_symbol;
    109
    1
      ccv_nnc_symbolic_graph_t* graph = ccv_nnc_simple_symbolic_graph(convnet, (ccv_nnc_tensor_t*)sliced, c, &source_symbol, &dest_symbol, &input_symbol, &output_symbol, w_symbols, bias_symbols);
    110
    1
      ccv_nnc_graph_t* run_graph = 0;
    111
    1
      ccv_nnc_tensor_arena_t* tensor_arena = 0;
    112
    1
      ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
    113
    1
      SYMBOLIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
    114
    1
      ccv_nnc_symbolic_graph_compile(graph, ccv_nnc_default_compile_params, TENSOR_BIND_MAP(KV(input_symbol, (ccv_nnc_tensor_t*)sliced), KV(output_symbol, c)), 0, 0, GRAPH_EXEC_SYMBOL_LIST(source_symbol), GRAPH_EXEC_SYMBOL_LIST(dest_symbol), &run_graph, &tensor_arena, &graph_exec_arena);
    115
    1
      GRAPH_GEN(run_graph, CCV_NNC_LONG_DOT_GRAPH);
    116
    1
      REQUIRE(ccv_nnc_tensor_arena_size(tensor_arena) <= 471513504, "the allocated size should be smaller than what we previously got");
    117
    1
      int i;
    118
    22
      for (i = 0; i < convnet->count; i++21)
    119
    21
      {
    120
    21
        ccv_convnet_layer_t* layer = convnet->layers + i;
    121
    21
        if (layer->type == CCV_CONVNET_CONVOLUTIONAL)
    122
    13
        {
    123
    13
          ccv_nnc_tensor_t* w = ccv_nnc_tensor_from_symbol(tensor_arena, w_symbols[i]);
    124
    13
          memcpy(w->data.f32, layer->w, layer->wnum * sizeof(float));
    125
    13
          ccv_nnc_tensor_t* bias = ccv_nnc_tensor_from_symbol(tensor_arena, bias_symbols[i]);
    126
    13
          memcpy(bias->data.f32, layer->bias, layer->net.convolutional.count * sizeof(float));
    127
    13
        } else if (8layer->type == CCV_CONVNET_FULL_CONNECT8) {
    128
    3
          ccv_nnc_tensor_t* w = ccv_nnc_tensor_from_symbol(tensor_arena, w_symbols[i]);
    129
    3
          memcpy(w->data.f32, layer->w, layer->wnum * sizeof(float));
    130
    3
          ccv_nnc_tensor_t* bias = ccv_nnc_tensor_from_symbol(tensor_arena, bias_symbols[i]);
    131
    3
          memcpy(bias->data.f32, layer->bias, layer->net.full_connect.count * sizeof(float));
    132
    3
        }
    133
    21
      }
    134
    1
      ccv_nnc_graph_autotune(run_graph, 0, 0, GRAPH_EXEC_LIST(ccv_nnc_graph_exec_from_symbol(graph_exec_arena, source_symbol)), GRAPH_EXEC_LIST(ccv_nnc_graph_exec_from_symbol(graph_exec_arena, dest_symbol)));
    135
      // Repopulate the weight. Since the weight tensor may be reused, need to repopulate every time run.
    136
      // To avoid this, bind the weights directly or declare as const.
    137
    22
      for (i = 0; i < convnet->count; i++21)
    138
    21
      {
    139
    21
        ccv_convnet_layer_t* layer = convnet->layers + i;
    140
    21
        if (layer->type == CCV_CONVNET_CONVOLUTIONAL)
    141
    13
        {
    142
    13
          ccv_nnc_tensor_t* w = ccv_nnc_tensor_from_symbol(tensor_arena, w_symbols[i]);
    143
    13
          memcpy(w->data.f32, layer->w, layer->wnum * sizeof(float));
    144
    13
          ccv_nnc_tensor_t* bias = ccv_nnc_tensor_from_symbol(tensor_arena, bias_symbols[i]);
    145
    13
          memcpy(bias->data.f32, layer->bias, layer->net.convolutional.count * sizeof(float));
    146
    13
        } else if (8layer->type == CCV_CONVNET_FULL_CONNECT8) {
    147
    3
          ccv_nnc_tensor_t* w = ccv_nnc_tensor_from_symbol(tensor_arena, w_symbols[i]);
    148
    3
          memcpy(w->data.f32, layer->w, layer->wnum * sizeof(float));
    149
    3
          ccv_nnc_tensor_t* bias = ccv_nnc_tensor_from_symbol(tensor_arena, bias_symbols[i]);
    150
    3
          memcpy(bias->data.f32, layer->bias, layer->net.full_connect.count * sizeof(float));
    151
    3
        }
    152
    21
      }
    153
    1
      ccv_nnc_graph_run(run_graph, 0, GRAPH_EXEC_LIST(ccv_nnc_graph_exec_from_symbol(graph_exec_arena, source_symbol)), GRAPH_EXEC_LIST(ccv_nnc_graph_exec_from_symbol(graph_exec_arena, dest_symbol)), 0, 0);
    154
    1
      REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, b->data.f32, c->data.f32, 1000, 1e-4, "output should be the same from convnet and from the symbolic graph");
    155
    1
      ccv_nnc_tensor_free(c);
    156
    1
      ccv_matrix_free(sliced);
    157
    1
      ccv_matrix_free(b);
    158
    1
      ccv_nnc_symbolic_graph_free(graph);
    159
    1
      ccv_nnc_graph_free(run_graph);
    160
    1
      ccv_nnc_tensor_arena_free(tensor_arena);
    161
    1
      ccv_nnc_graph_exec_arena_free(graph_exec_arena);
    162
    1
      ccfree(w_symbols);
    163
    1
      ccfree(bias_symbols);
    164
    1
      ccv_convnet_free(convnet);
    165
    1
    }
    166
    #endif
    167
    168
    #include "case_main.h"