├── .gitmodules ├── README.md ├── .gitignore ├── .editorconfig └── tests ├── test0.zig ├── test3.zig ├── test2.zig └── test1.zig /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/ggml"] 2 | path = thirdparty/ggml 3 | url = https://github.com/ggerganov/ggml.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ggml-zig 2 | 3 | Zig bindings for [ggml: Tensor library for machine learning](https://github.com/ggerganov/ggml) . 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | .DS_Store 4 | .build/ 5 | .cache/ 6 | .direnv/ 7 | .envrc 8 | .swiftpm 9 | .venv 10 | .clang-tidy 11 | .vs/ 12 | .vscode/ 13 | 14 | zig-out/ 15 | zig-cache/ 16 | 17 | *.dot 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file, utf-8 charset 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | charset = utf-8 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [Makefile] 16 | indent_style = tab 17 | 18 | [*.txt] 19 | insert_final_newline = unset 20 | 21 | [*.md] 22 | insert_final_newline = unset 23 | -------------------------------------------------------------------------------- /tests/test0.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const c = @cImport({ 3 | @cInclude("ggml/ggml.h"); 4 | }); 5 | 6 | pub fn main() !void { 7 | const params = .{ 8 | .mem_size = 128*1024*1024, 9 | .mem_buffer = null, 10 | .no_alloc = false, 11 | }; 12 | 13 | const ctx0 = c.ggml_init(params); 14 | defer c.ggml_free(ctx0); 15 | 16 | const t1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 10); 17 | const t2 = c.ggml_new_tensor_2d(ctx0, c.GGML_TYPE_I16, 10, 20); 18 | const t3 = c.ggml_new_tensor_3d(ctx0, c.GGML_TYPE_I32, 10, 20, 30); 19 | 20 | try std.testing.expect(t1.*.n_dims == 1); 21 | try std.testing.expect(t1.*.ne[0] == 10); 22 | try std.testing.expect(t1.*.nb[1] == 10*@sizeOf(f32)); 23 | 24 | try std.testing.expect(t2.*.n_dims == 2); 25 | try std.testing.expect(t2.*.ne[0] == 10); 26 | try std.testing.expect(t2.*.ne[1] == 20); 27 | try std.testing.expect(t2.*.nb[1] == 10*@sizeOf(i16)); 28 | try std.testing.expect(t2.*.nb[2] == 10*20*@sizeOf(i16)); 29 | 30 | try std.testing.expect(t3.*.n_dims == 3); 31 | try std.testing.expect(t3.*.ne[0] == 10); 32 | try std.testing.expect(t3.*.ne[1] == 20); 33 | try std.testing.expect(t3.*.ne[2] == 30); 34 | try std.testing.expect(t3.*.nb[1] == 10*@sizeOf(i32)); 35 | try std.testing.expect(t3.*.nb[2] == 10*20*@sizeOf(i32)); 36 | try std.testing.expect(t3.*.nb[3] == 10*20*30*@sizeOf(i32)); 37 | 38 | c.ggml_print_objects(ctx0); 39 | 40 | _ = try std.io.getStdIn().reader().readByte(); 41 | } 42 | -------------------------------------------------------------------------------- /tests/test3.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Thread = std.Thread; 3 | const c = @cImport({ 4 | @cInclude("stdlib.h"); 5 | @cInclude("ggml/ggml.h"); 6 | }); 7 | 8 | fn is_close(a: f32, b: f32, epsilon: f32) bool { 9 | return std.math.fabs(a - b) < epsilon; 10 | } 11 | 12 | pub fn main() !void { 13 | const params = .{ 14 | .mem_size = 128*1024*1024, 15 | .mem_buffer = null, 16 | .no_alloc = false, 17 | }; 18 | 19 | var opt_params = c.ggml_opt_default_params(c.GGML_OPT_LBFGS); 20 | 21 | const nthreads = try Thread.getCpuCount(); 22 | opt_params.n_threads = @intCast(nthreads); 23 | 24 | const NP = 1 << 12; 25 | const NF = 1 << 8; 26 | 27 | const ctx0 = c.ggml_init(params); 28 | defer c.ggml_free(ctx0); 29 | 30 | const F = c.ggml_new_tensor_2d(ctx0, c.GGML_TYPE_F32, NF, NP); 31 | const l = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, NP); 32 | 33 | // regularization weight 34 | const lambda = c.ggml_new_f32(ctx0, 1e-5); 35 | 36 | c.srand(0); 37 | 38 | const l_data_pointer: [*]f32 = @ptrCast(@alignCast(l.*.data)); 39 | const f_data_pointer: [*]f32 = @ptrCast(@alignCast(F.*.data)); 40 | for (0..NP) |j| { 41 | const ll = if (j < NP/2) @as(f32, 1.0) else @as(f32, -1.0); 42 | l_data_pointer[j] = ll; 43 | 44 | for (0..NF) |i| { 45 | const c_rand: f32 = @floatFromInt(c.rand()); 46 | f_data_pointer[j*NF + i] = 47 | ((if (ll > 0 and i < NF/2) @as(f32, 1.0) else 48 | if (ll < 0 and i >= NF/2) @as(f32, 1.0) else @as(f32, 0.0)) + 49 | (c_rand/c.RAND_MAX - 0.5) * 0.1) / (0.5 * NF); 50 | } 51 | } 52 | 53 | { 54 | // initial guess 55 | const x = c.ggml_set_f32(c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, NF), 0.0); 56 | 57 | c.ggml_set_param(ctx0, x); 58 | 59 | // f = sum[(fj*x - l)^2]/n + lambda*|x^2| 60 | const f = 61 | c.ggml_add(ctx0, 62 | c.ggml_div(ctx0, 63 | c.ggml_sum(ctx0, 64 | c.ggml_sqr(ctx0, 65 | c.ggml_sub(ctx0, 66 | c.ggml_mul_mat(ctx0, F, x), 67 | l) 68 | ) 69 | ), 70 | c.ggml_new_f32(ctx0, @as(f32, NP)) 71 | ), 72 | c.ggml_mul(ctx0, 73 | c.ggml_sum(ctx0, c.ggml_sqr(ctx0, x)), 74 | lambda) 75 | ); 76 | 77 | const res = c.ggml_opt(null, opt_params, f); 78 | 79 | try std.testing.expect(res == c.GGML_OPT_OK); 80 | 81 | const x_data_pointer: [*]f32 = @ptrCast(@alignCast(x.*.data)); 82 | // print results 83 | for (0..16) |i| { 84 | std.debug.print("x[{d:3}] = {d:.6}\n", .{i, x_data_pointer[i]}); 85 | } 86 | std.debug.print("...\n", .{}); 87 | for (NF - 16..NF) |i| { 88 | std.debug.print("x[{d:3}] = {d:.6}\n", .{i, x_data_pointer[i]}); 89 | } 90 | std.debug.print("\n", .{}); 91 | 92 | for (0..NF) |i| { 93 | if (i < NF/2) { 94 | try std.testing.expect(is_close(x_data_pointer[i], 1.0, 1e-2)); 95 | } else { 96 | try std.testing.expect(is_close(x_data_pointer[i], -1.0, 1e-2)); 97 | } 98 | } 99 | } 100 | 101 | _ = try std.io.getStdIn().reader().readByte(); 102 | } 103 | -------------------------------------------------------------------------------- /tests/test2.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Thread = std.Thread; 3 | const c = @cImport({ 4 | @cInclude("ggml/ggml.h"); 5 | }); 6 | 7 | fn is_close(a: f32, b: f32, epsilon: f32) bool { 8 | return std.math.fabs(a - b) < epsilon; 9 | } 10 | 11 | pub fn main() !void { 12 | const params = .{ 13 | .mem_size = 128*1024*1024, 14 | .mem_buffer = null, 15 | .no_alloc = false, 16 | }; 17 | 18 | var opt_params = c.ggml_opt_default_params(c.GGML_OPT_LBFGS); 19 | 20 | const nthreads = try Thread.getCpuCount(); 21 | opt_params.n_threads = @intCast(nthreads); 22 | std.debug.print("test2: n_threads:{}\n", .{opt_params.n_threads}); 23 | 24 | const xi = [_]f32{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; 25 | const yi = [_]f32{ 15.0, 25.0, 35.0, 45.0, 55.0, 65.0, 75.0, 85.0, 95.0, 105.0 }; 26 | 27 | const n = xi.len; 28 | 29 | const ctx0 = c.ggml_init(params); 30 | defer c.ggml_free(ctx0); 31 | 32 | const x = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, n); 33 | const y = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, n); 34 | 35 | for (0..n) |i| { 36 | const x_data_pointer: [*]f32 = @ptrCast(@alignCast(x.*.data)); 37 | x_data_pointer[i] = xi[i]; 38 | const y_data_pointer: [*]f32 = @ptrCast(@alignCast(y.*.data)); 39 | y_data_pointer[i] = yi[i]; 40 | } 41 | 42 | { 43 | const t0 = c.ggml_new_f32(ctx0, 0.0); 44 | const t1 = c.ggml_new_f32(ctx0, 0.0); 45 | 46 | // initialize auto-diff parameters: 47 | _ = c.ggml_set_param(ctx0, t0); 48 | _ = c.ggml_set_param(ctx0, t1); 49 | 50 | // f = sum_i[(t0 + t1*x_i - y_i)^2]/(2n) 51 | const f = 52 | c.ggml_div(ctx0, 53 | c.ggml_sum(ctx0, 54 | c.ggml_sqr(ctx0, 55 | c.ggml_sub(ctx0, 56 | c.ggml_add(ctx0, 57 | c.ggml_mul(ctx0, x, c.ggml_repeat(ctx0, t1, x)), 58 | c.ggml_repeat(ctx0, t0, x)), 59 | y) 60 | ) 61 | ), 62 | c.ggml_new_f32(ctx0, @as(f32, 2.0)*n)); 63 | 64 | const res = c.ggml_opt(null, opt_params, f); 65 | 66 | std.debug.print("t0 = {d:.6}\n", .{c.ggml_get_f32_1d(t0, 0)}); 67 | std.debug.print("t1 = {d:.6}\n", .{c.ggml_get_f32_1d(t1, 0)}); 68 | 69 | try std.testing.expect(res == c.GGML_OPT_OK); 70 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t0, 0), 5.0, 1e-3)); 71 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t1, 0), 10.0, 1e-3)); 72 | } 73 | 74 | { 75 | const t0 = c.ggml_new_f32(ctx0, -1.0); 76 | const t1 = c.ggml_new_f32(ctx0, 9.0); 77 | 78 | _ = c.ggml_set_param(ctx0, t0); 79 | _ = c.ggml_set_param(ctx0, t1); 80 | 81 | // f = 0.5*sum_i[abs(t0 + t1*x_i - y_i)]/n 82 | const f = 83 | c.ggml_mul(ctx0, 84 | c.ggml_new_f32(ctx0, @as(f32, 1.0)/(2*n)), 85 | c.ggml_sum(ctx0, 86 | c.ggml_abs(ctx0, 87 | c.ggml_sub(ctx0, 88 | c.ggml_add(ctx0, 89 | c.ggml_mul(ctx0, x, c.ggml_repeat(ctx0, t1, x)), 90 | c.ggml_repeat(ctx0, t0, x)), 91 | y) 92 | ) 93 | ) 94 | ); 95 | 96 | 97 | const res = c.ggml_opt(null, opt_params, f); 98 | 99 | try std.testing.expect(res == c.GGML_OPT_OK); 100 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t0, 0), 5.0, 1e-2)); 101 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t1, 0), 10.0, 1e-2)); 102 | } 103 | 104 | { 105 | const t0 = c.ggml_new_f32(ctx0, 5.0); 106 | const t1 = c.ggml_new_f32(ctx0, -4.0); 107 | 108 | _ = c.ggml_set_param(ctx0, t0); 109 | _ = c.ggml_set_param(ctx0, t1); 110 | 111 | // f = t0^2 + t1^2 112 | const f = 113 | c.ggml_add(ctx0, 114 | c.ggml_sqr(ctx0, t0), 115 | c.ggml_sqr(ctx0, t1) 116 | ); 117 | 118 | const res = c.ggml_opt(null, opt_params, f); 119 | 120 | try std.testing.expect(res == c.GGML_OPT_OK); 121 | try std.testing.expect(is_close(c.ggml_get_f32_1d(f, 0), 0.0, 1e-3)); 122 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t0, 0), 0.0, 1e-3)); 123 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t1, 0), 0.0, 1e-3)); 124 | } 125 | 126 | ///////////////////////////////////////// 127 | 128 | { 129 | const t0 = c.ggml_new_f32(ctx0, -7.0); 130 | const t1 = c.ggml_new_f32(ctx0, 8.0); 131 | 132 | _ = c.ggml_set_param(ctx0, t0); 133 | _ = c.ggml_set_param(ctx0, t1); 134 | 135 | // f = (t0 + 2*t1 - 7)^2 + (2*t0 + t1 - 5)^2 136 | const f = 137 | c.ggml_add(ctx0, 138 | c.ggml_sqr(ctx0, 139 | c.ggml_sub(ctx0, 140 | c.ggml_add(ctx0, 141 | t0, 142 | c.ggml_mul(ctx0, t1, c.ggml_new_f32(ctx0, 2.0))), 143 | c.ggml_new_f32(ctx0, 7.0) 144 | ) 145 | ), 146 | c.ggml_sqr(ctx0, 147 | c.ggml_sub(ctx0, 148 | c.ggml_add(ctx0, 149 | c.ggml_mul(ctx0, t0, c.ggml_new_f32(ctx0, 2.0)), 150 | t1), 151 | c.ggml_new_f32(ctx0, 5.0) 152 | ) 153 | ) 154 | ); 155 | 156 | const res = c.ggml_opt(null, opt_params, f); 157 | 158 | try std.testing.expect(res == c.GGML_OPT_OK); 159 | try std.testing.expect(is_close(c.ggml_get_f32_1d(f, 0), 0.0, 1e-3)); 160 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t0, 0), 1.0, 1e-3)); 161 | try std.testing.expect(is_close(c.ggml_get_f32_1d(t1, 0), 3.0, 1e-3)); 162 | } 163 | 164 | _ = try std.io.getStdIn().reader().readByte(); 165 | } 166 | -------------------------------------------------------------------------------- /tests/test1.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const c = @cImport({ 3 | @cInclude("ggml/ggml.h"); 4 | }); 5 | 6 | pub fn main() !void { 7 | const params = .{ 8 | .mem_size = 128*1024*1024, 9 | .mem_buffer = null, 10 | .no_alloc = false, 11 | }; 12 | 13 | const ctx0 = c.ggml_init(params); 14 | defer c.ggml_free(ctx0); 15 | 16 | { 17 | const x = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 18 | 19 | c.ggml_set_param(ctx0, x); 20 | 21 | const a = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 22 | const b = c.ggml_mul(ctx0, x, x); 23 | const f = c.ggml_mul(ctx0, b, a); 24 | 25 | // a*x^2 26 | // 2*a*x 27 | 28 | c.ggml_print_objects(ctx0); 29 | 30 | const gf = c.ggml_build_forward(f); 31 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 32 | 33 | _ = c.ggml_set_f32(x, 2.0); 34 | _ = c.ggml_set_f32(a, 3.0); 35 | 36 | c.ggml_graph_reset(@constCast(&gf)); 37 | _ = c.ggml_set_f32(f.*.grad, 1.0); 38 | 39 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 40 | 41 | std.debug.print("f = {d:.6}\n", .{c.ggml_get_f32_1d(f, 0)}); 42 | std.debug.print("df/dx = {d:.6}\n", .{c.ggml_get_f32_1d(x.*.grad, 0)}); 43 | 44 | try std.testing.expect(c.ggml_get_f32_1d(f, 0) == 12.0); 45 | try std.testing.expect(c.ggml_get_f32_1d(x.*.grad, 0) == 12.0); 46 | 47 | _ = c.ggml_set_f32(x, 3.0); 48 | 49 | c.ggml_graph_reset(@constCast(&gf)); 50 | _ = c.ggml_set_f32(f.*.grad, 1.0); 51 | 52 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 53 | 54 | std.debug.print("f = {d:.6}\n", .{c.ggml_get_f32_1d(f, 0)}); 55 | std.debug.print("df/dx = {d:.6}\n", .{c.ggml_get_f32_1d(x.*.grad, 0)}); 56 | 57 | try std.testing.expect(c.ggml_get_f32_1d(f, 0) == 27.0); 58 | try std.testing.expect(c.ggml_get_f32_1d(x.*.grad, 0) == 18.0); 59 | 60 | c.ggml_graph_dump_dot(&gf, null, "test1-1-forward.dot"); 61 | c.ggml_graph_dump_dot(&gb, &gf, "test1-1-backward.dot"); 62 | } 63 | 64 | ///////////////////////////////////////////////////////////// 65 | 66 | { 67 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 68 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 69 | const x3 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 70 | 71 | _ = c.ggml_set_f32(x1, 3.0); 72 | _ = c.ggml_set_f32(x2, 1.0); 73 | _ = c.ggml_set_f32(x3, 0.0); 74 | 75 | c.ggml_set_param(ctx0, x1); 76 | c.ggml_set_param(ctx0, x2); 77 | 78 | const y = c.ggml_add(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x1, x2)); 79 | 80 | const gf = c.ggml_build_forward(y); 81 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 82 | 83 | c.ggml_graph_reset(@constCast(&gf)); 84 | _ = c.ggml_set_f32(y.*.grad, 1.0); 85 | 86 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 87 | 88 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 89 | std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)}); 90 | std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)}); 91 | 92 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 12.0); 93 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 7.0); 94 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 3.0); 95 | 96 | const g1 = x1.*.grad; 97 | const g2 = x2.*.grad; 98 | 99 | const gbb = c.ggml_build_backward(ctx0, @constCast(&gb), true); 100 | 101 | c.ggml_graph_reset(@constCast(&gb)); 102 | _ = c.ggml_set_f32(g1.*.grad, 1.0); 103 | _ = c.ggml_set_f32(g2.*.grad, 1.0); 104 | 105 | c.ggml_graph_compute(ctx0, @constCast(&gbb)); 106 | 107 | std.debug.print("H * [1, 1] = [ {d:.6} {d:.6} ]\n", .{c.ggml_get_f32_1d(x1.*.grad, 0), c.ggml_get_f32_1d(x2.*.grad, 0)}); 108 | 109 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 3.0); 110 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 1.0); 111 | 112 | c.ggml_graph_dump_dot(&gf, null, "test1-2-forward.dot"); 113 | c.ggml_graph_dump_dot(&gb, &gf, "test1-2-backward.dot"); 114 | } 115 | 116 | /////////////////////////////////////////////////////////////// 117 | 118 | { 119 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 120 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 121 | 122 | c.ggml_set_param(ctx0, x1); 123 | c.ggml_set_param(ctx0, x2); 124 | 125 | const y = c.ggml_mul(ctx0, c.ggml_add(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x1, x2)), x1); 126 | 127 | const gf = c.ggml_build_forward(y); 128 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 129 | 130 | _ = c.ggml_set_f32(x1, 3.0); 131 | _ = c.ggml_set_f32(x2, 4.0); 132 | 133 | c.ggml_graph_reset(@constCast(&gf)); 134 | _ = c.ggml_set_f32(y.*.grad, 1.0); 135 | 136 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 137 | 138 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 139 | std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)}); 140 | std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)}); 141 | 142 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 63.0); 143 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 51.0); 144 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 9.0); 145 | 146 | c.ggml_graph_dump_dot(&gf, null, "test1-3-forward.dot"); 147 | c.ggml_graph_dump_dot(&gb, &gf, "test1-3-backward.dot"); 148 | } 149 | 150 | /////////////////////////////////////////////////////////////// 151 | 152 | { 153 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 154 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 155 | const x3 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1); 156 | 157 | c.ggml_set_param(ctx0, x1); 158 | c.ggml_set_param(ctx0, x2); 159 | c.ggml_set_param(ctx0, x3); 160 | 161 | const y = c.ggml_mul(ctx0, c.ggml_mul(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x2, x2)), x3); 162 | 163 | const gf = c.ggml_build_forward(y); 164 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 165 | 166 | _ = c.ggml_set_f32(x1, 1.0); 167 | _ = c.ggml_set_f32(x2, 2.0); 168 | _ = c.ggml_set_f32(x3, 3.0); 169 | 170 | c.ggml_graph_reset(@constCast(&gf)); 171 | _ = c.ggml_set_f32(y.*.grad, 1.0); 172 | 173 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 174 | 175 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 176 | std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)}); 177 | std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)}); 178 | std.debug.print("df/dx3 = {d:.6}\n", .{c.ggml_get_f32_1d(x3.*.grad, 0)}); 179 | 180 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 12.0); 181 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 24.0); 182 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 12.0); 183 | try std.testing.expect(c.ggml_get_f32_1d(x3.*.grad, 0) == 4.0); 184 | 185 | const g1 = x1.*.grad; 186 | const g2 = x2.*.grad; 187 | const g3 = x3.*.grad; 188 | 189 | const gbb = c.ggml_build_backward(ctx0, @constCast(&gb), true); 190 | 191 | c.ggml_graph_reset(@constCast(&gb)); 192 | _ = c.ggml_set_f32(g1.*.grad, 1.0); 193 | _ = c.ggml_set_f32(g2.*.grad, 1.0); 194 | _ = c.ggml_set_f32(g3.*.grad, 1.0); 195 | 196 | c.ggml_graph_compute(ctx0, @constCast(&gbb)); 197 | 198 | std.debug.print("H * [1, 1, 1] = [ {d:.6} {d:.6} {d:.6}]\n", 199 | .{ 200 | c.ggml_get_f32_1d(x1.*.grad, 0), 201 | c.ggml_get_f32_1d(x2.*.grad, 0), 202 | c.ggml_get_f32_1d(x3.*.grad, 0), 203 | }); 204 | 205 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 56.0); 206 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 34.0); 207 | try std.testing.expect(c.ggml_get_f32_1d(x3.*.grad, 0) == 12.0); 208 | 209 | c.ggml_graph_dump_dot(&gf, null, "test1-4-forward.dot"); 210 | c.ggml_graph_dump_dot(&gb, &gf, "test1-4-backward.dot"); 211 | } 212 | 213 | /////////////////////////////////////////////////////////////// 214 | 215 | { 216 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 217 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 218 | 219 | c.ggml_set_param(ctx0, x1); 220 | c.ggml_set_param(ctx0, x2); 221 | 222 | const y = c.ggml_sum(ctx0, c.ggml_mul(ctx0, x1, x2)); 223 | 224 | const gf = c.ggml_build_forward(y); 225 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 226 | 227 | _ = c.ggml_set_f32(x1, 3.0); 228 | _ = c.ggml_set_f32(x2, 5.0); 229 | 230 | c.ggml_graph_reset(@constCast(&gf)); 231 | _ = c.ggml_set_f32(y.*.grad, 1.0); 232 | 233 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 234 | 235 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 236 | std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", 237 | .{ 238 | c.ggml_get_f32_1d(x1.*.grad, 0), 239 | c.ggml_get_f32_1d(x1.*.grad, 1), 240 | c.ggml_get_f32_1d(x1.*.grad, 2), 241 | }); 242 | std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", 243 | .{ 244 | c.ggml_get_f32_1d(x2.*.grad, 0), 245 | c.ggml_get_f32_1d(x2.*.grad, 1), 246 | c.ggml_get_f32_1d(x2.*.grad, 2), 247 | }); 248 | 249 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 45.0); 250 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 5.0); 251 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 3.0); 252 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1) == 5.0); 253 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1) == 3.0); 254 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2) == 5.0); 255 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2) == 3.0); 256 | 257 | c.ggml_graph_dump_dot(&gf, null, "test1-5-forward.dot"); 258 | c.ggml_graph_dump_dot(&gb, &gf, "test1-5-backward.dot"); 259 | } 260 | 261 | /////////////////////////////////////////////////////////////// 262 | 263 | { 264 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 265 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 266 | 267 | c.ggml_set_param(ctx0, x1); 268 | c.ggml_set_param(ctx0, x2); 269 | 270 | const y = 271 | c.ggml_sum(ctx0, 272 | c.ggml_add(ctx0, 273 | c.ggml_mul(ctx0, x1, x2), 274 | c.ggml_mul(ctx0, 275 | c.ggml_repeat(ctx0, c.ggml_new_f32(ctx0, -2.0), x1), 276 | c.ggml_mul(ctx0, x1, x1) 277 | ) 278 | ) 279 | ); 280 | 281 | const gf = c.ggml_build_forward(y); 282 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 283 | 284 | _ = c.ggml_set_f32(x1, 3.0); 285 | _ = c.ggml_set_f32(x2, 5.0); 286 | 287 | c.ggml_graph_reset(@constCast(&gf)); 288 | _ = c.ggml_set_f32(y.*.grad, 1.0); 289 | 290 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 291 | 292 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 293 | std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", 294 | .{ 295 | c.ggml_get_f32_1d(x1.*.grad, 0), 296 | c.ggml_get_f32_1d(x1.*.grad, 1), 297 | c.ggml_get_f32_1d(x1.*.grad, 2), 298 | }); 299 | std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", 300 | .{ 301 | c.ggml_get_f32_1d(x2.*.grad, 0), 302 | c.ggml_get_f32_1d(x2.*.grad, 1), 303 | c.ggml_get_f32_1d(x2.*.grad, 2), 304 | }); 305 | 306 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == -9.0); 307 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == -7.0); 308 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1) == -7.0); 309 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2) == -7.0); 310 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 3.0); 311 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1) == 3.0); 312 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2) == 3.0); 313 | 314 | c.ggml_graph_dump_dot(&gf, null, "test1-6-forward.dot"); 315 | c.ggml_graph_dump_dot(&gb, &gf, "test1-6-backward.dot"); 316 | } 317 | 318 | /////////////////////////////////////////////////////////////// 319 | 320 | { 321 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 322 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 323 | 324 | c.ggml_set_param(ctx0, x1); 325 | c.ggml_set_param(ctx0, x2); 326 | 327 | const y = 328 | c.ggml_sum(ctx0, 329 | c.ggml_sub(ctx0, 330 | c.ggml_mul(ctx0, x1, x2), 331 | c.ggml_mul(ctx0, 332 | c.ggml_mul(ctx0, x1, x1), 333 | c.ggml_repeat(ctx0, c.ggml_new_f32(ctx0, -2.0), x1) 334 | ) 335 | ) 336 | ); 337 | 338 | const gf = c.ggml_build_forward(y); 339 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 340 | 341 | _ = c.ggml_set_f32(x1, 3.0); 342 | _ = c.ggml_set_f32(x2, 5.0); 343 | 344 | c.ggml_graph_reset(@constCast(&gf)); 345 | _ = c.ggml_set_f32(y.*.grad, 1.0); 346 | 347 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 348 | 349 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 350 | std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", 351 | .{ 352 | c.ggml_get_f32_1d(x1.*.grad, 0), 353 | c.ggml_get_f32_1d(x1.*.grad, 1), 354 | c.ggml_get_f32_1d(x1.*.grad, 2), 355 | }); 356 | std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", 357 | .{ 358 | c.ggml_get_f32_1d(x2.*.grad, 0), 359 | c.ggml_get_f32_1d(x2.*.grad, 1), 360 | c.ggml_get_f32_1d(x2.*.grad, 2), 361 | }); 362 | 363 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 99.0); 364 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 17.0); 365 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1) == 17.0); 366 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2) == 17.0); 367 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 3.0); 368 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1) == 3.0); 369 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2) == 3.0); 370 | 371 | c.ggml_graph_dump_dot(&gf, null, "test1-7-forward.dot"); 372 | c.ggml_graph_dump_dot(&gb, &gf, "test1-7-backward.dot"); 373 | } 374 | 375 | /////////////////////////////////////////////////////////////// 376 | 377 | { 378 | const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 379 | const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3); 380 | 381 | c.ggml_set_param(ctx0, x1); 382 | c.ggml_set_param(ctx0, x2); 383 | 384 | const y = 385 | c.ggml_abs(ctx0, 386 | c.ggml_sub(ctx0, x1, x2) 387 | ); 388 | 389 | const gf = c.ggml_build_forward(y); 390 | const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false); 391 | 392 | _ = c.ggml_set_f32(x1, 3.0); 393 | _ = c.ggml_set_f32(x2, 5.0); 394 | 395 | c.ggml_graph_reset(@constCast(&gf)); 396 | _ = c.ggml_set_f32(y.*.grad, 1.0); 397 | 398 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 399 | 400 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 401 | std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", 402 | .{ 403 | c.ggml_get_f32_1d(x1.*.grad, 0), 404 | c.ggml_get_f32_1d(x1.*.grad, 1), 405 | c.ggml_get_f32_1d(x1.*.grad, 2), 406 | }); 407 | std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", 408 | .{ 409 | c.ggml_get_f32_1d(x2.*.grad, 0), 410 | c.ggml_get_f32_1d(x2.*.grad, 1), 411 | c.ggml_get_f32_1d(x2.*.grad, 2), 412 | }); 413 | 414 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 2.0); 415 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == -1.0); 416 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1) == -1.0); 417 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2) == -1.0); 418 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == 1.0); 419 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1) == 1.0); 420 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2) == 1.0); 421 | 422 | _ = c.ggml_set_f32(x1, 7.0); 423 | _ = c.ggml_set_f32(x2, 5.0); 424 | 425 | c.ggml_graph_reset(@constCast(&gf)); 426 | _ = c.ggml_set_f32(y.*.grad, 1.0); 427 | 428 | c.ggml_graph_compute(ctx0, @constCast(&gb)); 429 | 430 | std.debug.print("y = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)}); 431 | std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", 432 | .{ 433 | c.ggml_get_f32_1d(x1.*.grad, 0), 434 | c.ggml_get_f32_1d(x1.*.grad, 1), 435 | c.ggml_get_f32_1d(x1.*.grad, 2), 436 | }); 437 | std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", 438 | .{ 439 | c.ggml_get_f32_1d(x2.*.grad, 0), 440 | c.ggml_get_f32_1d(x2.*.grad, 1), 441 | c.ggml_get_f32_1d(x2.*.grad, 2), 442 | }); 443 | 444 | try std.testing.expect(c.ggml_get_f32_1d(y, 0) == 2.0); 445 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0) == 1.0); 446 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1) == 1.0); 447 | try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2) == 1.0); 448 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0) == -1.0); 449 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1) == -1.0); 450 | try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2) == -1.0); 451 | 452 | c.ggml_graph_dump_dot(&gf, null, "test1-8-forward.dot"); 453 | c.ggml_graph_dump_dot(&gb, &gf, "test1-8-backward.dot"); 454 | } 455 | 456 | _ = try std.io.getStdIn().reader().readByte(); 457 | } 458 | --------------------------------------------------------------------------------