]> git.djapps.eu Git - pkg/ggml/sources/ggml/commitdiff
zig : add tests by zig (#307)
authorsjinzh <redacted>
Mon, 26 Jun 2023 20:48:31 +0000 (04:48 +0800)
committerGitHub <redacted>
Mon, 26 Jun 2023 20:48:31 +0000 (23:48 +0300)
* update build.zig

* zig : add tests by zig

.gitignore
build.zig
tests/test0.zig [new file with mode: 0644]
tests/test1.zig [new file with mode: 0644]

index 093031907516335187d3112ed958cdd11047740f..698b04a98d824199db08c92605e1662894c77181 100644 (file)
@@ -18,4 +18,6 @@ src/arm_neon.h
 tests/arm_neon.h
 
 zig-out/
-zig-cache/
\ No newline at end of file
+zig-cache/
+
+*.dot
\ No newline at end of file
index 34582cec6159a8851c4867cacbc05af4057300fc..87151a8851aa682d42ed1bf15e4abf5ede1ae127 100644 (file)
--- a/build.zig
+++ b/build.zig
@@ -2,24 +2,26 @@ const std = @import("std");
 
 // Zig Version: 0.11.0-dev.3798+a5e15eced
 // Zig Build Command: zig build
-// Zig Run Command:    
-//     zig build run_dolly-v2             
-//     zig build run_gpt-2                  
-//     zig build run_gpt-j                   
-//     zig build run_gpt-neox                 
-//     zig build run_mnist                    
-//     zig build run_mpt                     
-//     zig build run_replit                  
-//     zig build run_starcoder                
-//     zig build run_test-grad0               
-//     zig build run_test-mul-mat0            
-//     zig build run_test-mul-mat2            
-//     zig build run_test-opt                 
-//     zig build run_test-vec1                
-//     zig build run_test0                   
-//     zig build run_test1                    
-//     zig build run_test2                    
-//     zig build run_test3                    
+// Zig Run Command: zig build -h  
+//     zig build run_dolly-v2           
+//     zig build run_gpt-2                
+//     zig build run_gpt-j                 
+//     zig build run_gpt-neox               
+//     zig build run_mnist                  
+//     zig build run_mpt                 
+//     zig build run_replit        
+//     zig build run_starcoder            
+//     zig build run_test-grad0            
+//     zig build run_test-mul-mat0          
+//     zig build run_test-mul-mat2         
+//     zig build run_test-opt               
+//     zig build run_test-vec1               
+//     zig build run_test0                 
+//     zig build run_test1                  
+//     zig build run_test2              
+//     zig build run_test3
+//     zig build run_zig_test0
+//     zig build run_zig_test1     
 pub fn build(b: *std.build.Builder) void {
     const target = b.standardTargetOptions(.{});
     const optimize = b.standardOptimizeOption(.{});
@@ -110,4 +112,27 @@ pub fn build(b: *std.build.Builder) void {
         const run_step = b.step("run_" ++ name, "Run tests");
         run_step.dependOn(&run_cmd.step);
     }
+
+    // zig_tests
+    const zig_tests = .{
+        "test0",
+        "test1",
+    };
+    inline for (zig_tests) |name| {
+        const exe = b.addExecutable(.{
+            .name = name,
+            .root_source_file = .{ .path = std.fmt.comptimePrint("tests/{s}.zig", .{name}) },
+            .target = target,
+            .optimize = optimize,
+        });
+        exe.addIncludePath("./include");
+        exe.addIncludePath("./include/ggml");
+        exe.linkLibrary(lib);
+        b.installArtifact(exe);
+        const run_cmd = b.addRunArtifact(exe);
+        run_cmd.step.dependOn(b.getInstallStep());
+        if (b.args) |args| run_cmd.addArgs(args);
+        const run_step = b.step("run_zig_" ++ name, "Run zig_tests");
+        run_step.dependOn(&run_cmd.step);
+    }
 }
\ No newline at end of file
diff --git a/tests/test0.zig b/tests/test0.zig
new file mode 100644 (file)
index 0000000..8246a97
--- /dev/null
@@ -0,0 +1,43 @@
+const std = @import("std");\r
+const c = @cImport({\r
+    @cInclude("stdio.h");\r
+    @cInclude("stdlib.h");\r
+    @cInclude("ggml/ggml.h");\r
+});\r
+\r
+pub fn main() !void {\r
+    const params = .{\r
+        .mem_size   = 128*1024*1024,\r
+        .mem_buffer = null,\r
+        .no_alloc   = false,\r
+    };\r
+\r
+    const ctx0 = c.ggml_init(params);\r
+    defer c.ggml_free(ctx0);\r
+\r
+    const t1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 10);\r
+    const t2 = c.ggml_new_tensor_2d(ctx0, c.GGML_TYPE_I16, 10, 20);\r
+    const t3 = c.ggml_new_tensor_3d(ctx0, c.GGML_TYPE_I32, 10, 20, 30);\r
+\r
+    try std.testing.expect(t1.*.n_dims == 1);\r
+    try std.testing.expect(t1.*.ne[0]  == 10);\r
+    try std.testing.expect(t1.*.nb[1]  == 10*@sizeOf(f32));\r
+\r
+    try std.testing.expect(t2.*.n_dims == 2);\r
+    try std.testing.expect(t2.*.ne[0]  == 10);\r
+    try std.testing.expect(t2.*.ne[1]  == 20);\r
+    try std.testing.expect(t2.*.nb[1]  == 10*@sizeOf(i16));\r
+    try std.testing.expect(t2.*.nb[2]  == 10*20*@sizeOf(i16));\r
+\r
+    try std.testing.expect(t3.*.n_dims == 3);\r
+    try std.testing.expect(t3.*.ne[0]  == 10);\r
+    try std.testing.expect(t3.*.ne[1]  == 20);\r
+    try std.testing.expect(t3.*.ne[2]  == 30);\r
+    try std.testing.expect(t3.*.nb[1]  == 10*@sizeOf(i32));\r
+    try std.testing.expect(t3.*.nb[2]  == 10*20*@sizeOf(i32));\r
+    try std.testing.expect(t3.*.nb[3]  == 10*20*30*@sizeOf(i32));\r
+\r
+    c.ggml_print_objects(ctx0);\r
+\r
+    _ = try std.io.getStdIn().reader().readByte();\r
+}\r
diff --git a/tests/test1.zig b/tests/test1.zig
new file mode 100644 (file)
index 0000000..e40c37e
--- /dev/null
@@ -0,0 +1,459 @@
+const std = @import("std");\r
+const c = @cImport({\r
+    @cInclude("stdio.h");\r
+    @cInclude("stdlib.h");\r
+    @cInclude("ggml/ggml.h");\r
+});\r
+\r
+pub fn main() !void {\r
+    const params = .{\r
+        .mem_size   = 128*1024*1024,\r
+        .mem_buffer = null,\r
+        .no_alloc   = false,\r
+    };\r
+\r
+    const ctx0 = c.ggml_init(params);\r
+    defer c.ggml_free(ctx0);\r
+\r
+    {\r
+        const x = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+\r
+        c.ggml_set_param(ctx0, x);\r
+\r
+        const a = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const b = c.ggml_mul(ctx0, x, x);\r
+        const f = c.ggml_mul(ctx0, b, a);\r
+\r
+        // a*x^2\r
+        // 2*a*x\r
+\r
+        c.ggml_print_objects(ctx0);\r
+\r
+        const gf = c.ggml_build_forward(f);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x, 2.0);\r
+        _ = c.ggml_set_f32(a, 3.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(f.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("f     = {d:.6}\n", .{c.ggml_get_f32_1d(f, 0)});\r
+        std.debug.print("df/dx = {d:.6}\n", .{c.ggml_get_f32_1d(x.*.grad, 0)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(f, 0)          ==  12.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x.*.grad, 0)   ==  12.0);\r
+\r
+        _ = c.ggml_set_f32(x, 3.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(f.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("f     = {d:.6}\n", .{c.ggml_get_f32_1d(f, 0)});\r
+        std.debug.print("df/dx = {d:.6}\n", .{c.ggml_get_f32_1d(x.*.grad, 0)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(f, 0)          ==  27.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x.*.grad, 0)   ==  18.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-1-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-1-backward.dot");\r
+    }\r
+\r
+    /////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const x3 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 1.0);\r
+        _ = c.ggml_set_f32(x3, 0.0);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y = c.ggml_add(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x1, x2));\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)});\r
+        std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  12.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  7.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  3.0);\r
+\r
+        const g1 = x1.*.grad;\r
+        const g2 = x2.*.grad;\r
+\r
+        const gbb = c.ggml_build_backward(ctx0, @constCast(&gb), true);\r
+\r
+        c.ggml_graph_reset(@constCast(&gb));\r
+        _ = c.ggml_set_f32(g1.*.grad, 1.0);\r
+        _ = c.ggml_set_f32(g2.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gbb));\r
+\r
+        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)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  1.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-2-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-2-backward.dot");\r
+    }\r
+    \r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y = c.ggml_mul(ctx0, c.ggml_add(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x1, x2)), x1);\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 4.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)});\r
+        std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  63.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  51.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  9.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-3-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-3-backward.dot");\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+        const x3 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 1);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+        c.ggml_set_param(ctx0, x3);\r
+\r
+        const y = c.ggml_mul(ctx0, c.ggml_mul(ctx0, c.ggml_mul(ctx0, x1, x1), c.ggml_mul(ctx0, x2, x2)), x3);\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 1.0);\r
+        _ = c.ggml_set_f32(x2, 2.0);\r
+        _ = c.ggml_set_f32(x3, 3.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6}\n", .{c.ggml_get_f32_1d(x1.*.grad, 0)});\r
+        std.debug.print("df/dx2 = {d:.6}\n", .{c.ggml_get_f32_1d(x2.*.grad, 0)});\r
+        std.debug.print("df/dx3 = {d:.6}\n", .{c.ggml_get_f32_1d(x3.*.grad, 0)});\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  12.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  24.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  12.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x3.*.grad, 0)  ==  4.0);      \r
+\r
+        const g1 = x1.*.grad;\r
+        const g2 = x2.*.grad;\r
+        const g3 = x3.*.grad;\r
+\r
+        const gbb = c.ggml_build_backward(ctx0, @constCast(&gb), true);\r
+\r
+        c.ggml_graph_reset(@constCast(&gb));\r
+        _ = c.ggml_set_f32(g1.*.grad, 1.0);\r
+        _ = c.ggml_set_f32(g2.*.grad, 1.0);\r
+        _ = c.ggml_set_f32(g3.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gbb));\r
+\r
+        std.debug.print("H * [1, 1, 1] = [ {d:.6} {d:.6} {d:.6}]\n", \r
+            .{  \r
+                c.ggml_get_f32_1d(x1.*.grad, 0), \r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x3.*.grad, 0),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  56.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  34.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x3.*.grad, 0)  ==  12.0);  \r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-4-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-4-backward.dot");\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y = c.ggml_sum(ctx0, c.ggml_mul(ctx0, x1, x2));\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 5.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x1.*.grad, 0),\r
+                c.ggml_get_f32_1d(x1.*.grad, 1),\r
+                c.ggml_get_f32_1d(x1.*.grad, 2),\r
+            });\r
+        std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x2.*.grad, 1),\r
+                c.ggml_get_f32_1d(x2.*.grad, 2),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  45.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  5.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1)  ==  5.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2)  ==  5.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2)  ==  3.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-5-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-5-backward.dot");\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y =\r
+            c.ggml_sum(ctx0,\r
+                    c.ggml_add(ctx0,\r
+                        c.ggml_mul(ctx0, x1, x2),\r
+                        c.ggml_mul(ctx0,\r
+                            c.ggml_repeat(ctx0, c.ggml_new_f32(ctx0, -2.0), x1),\r
+                            c.ggml_mul(ctx0, x1, x1)\r
+                            )\r
+                        )\r
+                    );\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 5.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x1.*.grad, 0),\r
+                c.ggml_get_f32_1d(x1.*.grad, 1),\r
+                c.ggml_get_f32_1d(x1.*.grad, 2),\r
+            });\r
+        std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x2.*.grad, 1),\r
+                c.ggml_get_f32_1d(x2.*.grad, 2),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  -9.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  -7.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1)  ==  -7.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2)  ==  -7.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2)  ==  3.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-6-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-6-backward.dot");\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y =\r
+            c.ggml_sum(ctx0,\r
+                    c.ggml_sub(ctx0,\r
+                        c.ggml_mul(ctx0, x1, x2),\r
+                        c.ggml_mul(ctx0,\r
+                            c.ggml_mul(ctx0, x1, x1),\r
+                            c.ggml_repeat(ctx0, c.ggml_new_f32(ctx0, -2.0), x1)\r
+                            )\r
+                        )\r
+                    );\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 5.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x1.*.grad, 0),\r
+                c.ggml_get_f32_1d(x1.*.grad, 1),\r
+                c.ggml_get_f32_1d(x1.*.grad, 2),\r
+            });\r
+        std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x2.*.grad, 1),\r
+                c.ggml_get_f32_1d(x2.*.grad, 2),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  99.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  17.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1)  ==  17.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2)  ==  17.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1)  ==  3.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2)  ==  3.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-7-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-7-backward.dot");\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////\r
+\r
+    {\r
+        const x1 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+        const x2 = c.ggml_new_tensor_1d(ctx0, c.GGML_TYPE_F32, 3);\r
+\r
+        c.ggml_set_param(ctx0, x1);\r
+        c.ggml_set_param(ctx0, x2);\r
+\r
+        const y =\r
+            c.ggml_abs(ctx0,\r
+                    c.ggml_sub(ctx0, x1, x2)\r
+                    );\r
+\r
+        const gf = c.ggml_build_forward(y);\r
+        const gb = c.ggml_build_backward(ctx0, @constCast(&gf), false);\r
+\r
+        _ = c.ggml_set_f32(x1, 3.0);\r
+        _ = c.ggml_set_f32(x2, 5.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x1.*.grad, 0),\r
+                c.ggml_get_f32_1d(x1.*.grad, 1),\r
+                c.ggml_get_f32_1d(x1.*.grad, 2),\r
+            });\r
+        std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x2.*.grad, 1),\r
+                c.ggml_get_f32_1d(x2.*.grad, 2),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  2.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  -1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1)  ==  -1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2)  ==  -1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1)  ==  1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2)  ==  1.0);\r
+\r
+        _ = c.ggml_set_f32(x1, 7.0);\r
+        _ = c.ggml_set_f32(x2, 5.0);\r
+\r
+        c.ggml_graph_reset(@constCast(&gf));\r
+        _ = c.ggml_set_f32(y.*.grad, 1.0);\r
+\r
+        c.ggml_graph_compute(ctx0, @constCast(&gb));\r
+\r
+        std.debug.print("y      = {d:.6}\n", .{c.ggml_get_f32_1d(y, 0)});\r
+        std.debug.print("df/dx1 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x1.*.grad, 0),\r
+                c.ggml_get_f32_1d(x1.*.grad, 1),\r
+                c.ggml_get_f32_1d(x1.*.grad, 2),\r
+            });\r
+        std.debug.print("df/dx2 = {d:.6} {d:.6} {d:.6}\n", \r
+            .{\r
+                c.ggml_get_f32_1d(x2.*.grad, 0),\r
+                c.ggml_get_f32_1d(x2.*.grad, 1),\r
+                c.ggml_get_f32_1d(x2.*.grad, 2),\r
+            });\r
+\r
+        try std.testing.expect(c.ggml_get_f32_1d(y, 0)          ==  2.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 0)  ==  1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 1)  ==  1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x1.*.grad, 2)  ==  1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 0)  ==  -1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 1)  ==  -1.0);\r
+        try std.testing.expect(c.ggml_get_f32_1d(x2.*.grad, 2)  ==  -1.0);\r
+\r
+        c.ggml_graph_dump_dot(&gf, null, "test1-8-forward.dot");\r
+        c.ggml_graph_dump_dot(&gb, &gf,  "test1-8-backward.dot");\r
+    }\r
+\r
+    _ = try std.io.getStdIn().reader().readByte();\r
+}
\ No newline at end of file