## 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/unit/nnc/gradient.tests.c

    Line
    Count
    Source
    1
    #include "case.h"
    2
    #include "ccv_case.h"
    3
    #include <ccv.h>
    4
    #include <nnc/ccv_nnc.h>
    5
    #include <nnc/ccv_nnc_easy.h>
    6
    #include "3rdparty/dsfmt/dSFMT.h"
    7
    8
    TEST_SETUP()
    9
    {
    10
      ccv_nnc_init();
    11
    }
    12
    13
    // five-stencil constants
    14
    static double fs[4] = { 1, -8, 8, -1 };
    15
    static double fsh[4] = { -2, -1, 1, 2 };
    16
    17
    TEST_CASE("numerical gradient versus analytical gradient for convolutional network")
    18
    1
    {
    19
    1
      ccv_nnc_tensor_t* a = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 2), 0);
    20
    1
      ccv_nnc_tensor_t* b = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 31, 21, 4), 0);
    21
    1
      ccv_nnc_cmd_t forw_cmd = CMD_CONVOLUTION_FORWARD(1, 4, 5, 3, 2);
    22
    1
      ccv_nnc_hint_t hint = ccv_nnc_hint_auto(forw_cmd.info, a->info, b->info);
    23
    1
      ccv_nnc_tensor_t* w = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4, 5, 3, 2), 0);
    24
    1
      ccv_nnc_tensor_t* bias = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 4), 0);
    25
    1
      dsfmt_t dsfmt;
    26
    1
      dsfmt_init_gen_rand(&dsfmt, 1);
    27
    1
      int i, j;
    28
    121
      for (i = 0; i < 2 * 3 * 5 * 4; i++120)
    29
    120
        w->data.f32[i] = (dsfmt_genrand_open_close(&dsfmt) * 2 - 1) * 1.41421356237 / sqrtf(21 * 31 * 2 + 21 * 31 * 4);
    30
    1
      float denom = (21 * 31 * 2 - 1) * 21 * 31 * 2;
    31
    1.30k
      for (i = 0; i < 21 * 31 * 2; i++1.30k)
    32
    1.30k
        a->data.f32[i] = (float)(i - 21 * 31) / denom;
    33
    5
      for (i = 0; i < 4; i++4)
    34
    4
        bias->data.f32[i] = 0;
    35
    1
      ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0);
    36
    1
      ccv_nnc_tensor_t* ba = ccv_nnc_tensor_new(b->data.f32, CPU_TENSOR_NHWC(32F, 31 * 21 * 4), 0);
    37
    1
      ccv_nnc_tensor_t* m = ccv_nnc_tensor_new(0, ba->info, 0);
    38
    1
      ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0);
    39
    1
      ccv_nnc_cmd_t back_cmd = CMD_CONVOLUTION_BACKWARD(1, 4, 5, 3, 2);
    40
    1
      ccv_nnc_tensor_t* gw = ccv_nnc_tensor_new(0, w->info, 0);
    41
    1
      ccv_nnc_tensor_t* gbias = ccv_nnc_tensor_new(0, bias->info, 0);
    42
    1
      ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, b->info, 0);
    43
    1
      ccv_nnc_tensor_t* h = ccv_nnc_tensor_new(0, a->info, 0);
    44
    2.60k
      for (i = 0; i < 21 * 31 * 4; i++2.60k)
    45
    2.60k
        g->data.f32[i] = m->data.f32[i] - (i == 24);
    46
    1
      ccv_nnc_cmd_exec(back_cmd, hint, 0, TENSOR_LIST(g, a, w), TENSOR_LIST(h, gw, gbias), 0);
    47
      // Now doing numeric gradient computation
    48
    1
      static const double eps = 0.001;
    49
    1
      float* dw = (float*)ccmalloc(sizeof(float) * 2 * 3 * 5 * 4);
    50
    121
      for (i = 0; i < 2 * 3 * 5 * 4; i++120)
    51
    120
      {
    52
    120
        double vw = 0;
    53
    600
        for (j = 0; j < 4; j++480)
    54
    480
        {
    55
    480
          float old_w = w->data.f32[i];
    56
    480
          w->data.f32[i] += fsh[j] * eps;
    57
    480
          ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0);
    58
    480
          ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0);
    59
    480
          vw += -log(m->data.f32[24]) * fs[j];
    60
    480
          w->data.f32[i] = old_w;
    61
    480
        }
    62
    120
        dw[i] = vw / (12 * eps);
    63
    120
      }
    64
    1
      float* dbias = (float*)ccmalloc(sizeof(float) * 4);
    65
    5
      for (i = 0; i < 4; i++4)
    66
    4
      {
    67
    4
        dbias[i] = 0;
    68
    20
        for (j = 0; j < 4; j++16)
    69
    16
        {
    70
    16
          float old_bias = bias->data.f32[i];
    71
    16
          bias->data.f32[i] += fsh[j] * eps;
    72
    16
          ccv_nnc_cmd_exec(forw_cmd, hint, 0, TENSOR_LIST(a, w, bias), TENSOR_LIST(b), 0);
    73
    16
          ccv_nnc_cmd_exec(CMD_SOFTMAX_FORWARD(), hint, 0, TENSOR_LIST(ba), TENSOR_LIST(m), 0);
    74
    16
          dbias[i] += -logf(m->data.f32[24]) * fs[j];
    75
    16
          bias->data.f32[i] = old_bias;
    76
    16
        }
    77
    4
        dbias[i] *= 1.0 / (12 * eps);
    78
    4
      }
    79
    1
      REQUIRE_ARRAY_EQ_WITHIN_ANGLE_AND_MAGNITUDE(float, dw, gw->data.f32, 2 * 3 * 5 * 4, 30, 2e-1, "weight gradient from analytical method doesn't match the one from numerical method");
    80
    1
      REQUIRE_ARRAY_EQ_WITHIN_ANGLE_AND_MAGNITUDE(float, dbias, gbias->data.f32, 4, 30, 2e-1, "bias gradient from analytical method doesn't match the one from numerical method");
    81
    1
      ccfree(dw);
    82
    1
      ccfree(dbias);
    83
    1
      ccv_nnc_tensor_free(a);
    84
    1
      ccv_nnc_tensor_free(b);
    85
    1
      ccv_nnc_tensor_free(ba);
    86
    1
      ccv_nnc_tensor_free(m);
    87
    1
      ccv_nnc_tensor_free(g);
    88
    1
      ccv_nnc_tensor_free(h);
    89
    1
      ccv_nnc_tensor_free(w);
    90
    1
      ccv_nnc_tensor_free(bias);
    91
    1
      ccv_nnc_tensor_free(gw);
    92
    1
      ccv_nnc_tensor_free(gbias);
    93
    1
    }
    94
    95
    #include "case_main.h"