lib/accy/src/kernel/library/random/fold/philox/family.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const choir_abi = @import("choir_abi");
2 const random = @import("../../root.zig");
3
4 const base = random.base;
5 const block = random.block;
6 const runtime = @import("runtime.zig");
7 const types = @import("types.zig");
8
9 const std = base.std;
10 const artifact_product = base.artifact_product;
11 const shape = base.shape;
12 const entry = base.entry;
13 const kernel = base.kernel;
14 const tuning = base.tuning;
15 const runtimeExtentArgument = base.runtimeExtentArgument;
16
17 const philoxBlock = block.philoxBlock;
18 const uniformFromBits = block.uniformFromBits;
19 const randomRuntimeExtentBounds = base.randomRuntimeExtentBounds;
20 const randomShapeFamily = base.randomShapeFamily;
21 const randomFoldDerivedLaunch = base.randomFoldDerivedLaunch;
22
23 pub const philox_fold_family_version = types.philox_fold_family_version;
24 pub const PhiloxFold = types.PhiloxFold;
25 pub const PhiloxFoldRuntimeFamilyF32 = runtime.PhiloxFoldRuntimeFamilyF32;
26 pub const PhiloxFoldRuntimeFamilyI32 = runtime.PhiloxFoldRuntimeFamilyI32;
27
28 pub fn philoxFoldUniformReference(rounds: u32, output_index: u32, samples: u32, key: [2]u32) f32 {
29 var accumulator: f32 = 0.0;
30 for (0..samples) |sample| {
31 const words = philoxBlock(rounds, .{ output_index, @intCast(sample), 0, 0 }, key);
32 for (words) |word| accumulator += uniformFromBits(word);
33 }
34 return accumulator;
35 }
36
37 pub fn philoxFoldBitsReference(rounds: u32, output_index: u32, samples: u32, key: [2]u32) u32 {
38 var accumulator: u32 = 0;
39 for (0..samples) |sample| {
40 const words = philoxBlock(rounds, .{ output_index, @intCast(sample), 0, 0 }, key);
41 for (words) |word| accumulator ^= word;
42 }
43 return accumulator;
44 }
45
46 pub fn philoxFoldFamilyTarget(allocator: std.mem.Allocator, instance: PhiloxFold) ![]u8 {
47 return std.fmt.allocPrint(
48 allocator,
49 "accy.kernel.random.philox_fold_family_{d}r_{d}_{s}",
50 .{ instance.rounds, instance.threads, instance.dtype.name() },
51 );
52 }
53
54 pub fn philoxFoldFamilyEntryName(allocator: std.mem.Allocator, instance: PhiloxFold) ![]u8 {
55 return std.fmt.allocPrint(
56 allocator,
57 "accy_kernel_random_philox_fold_family_{d}r_{d}_{s}",
58 .{ instance.rounds, instance.threads, instance.dtype.name() },
59 );
60 }
61 pub fn philoxFoldRuntimeArguments(instance: PhiloxFold) ![4]choir_abi.ScalarArgument {
62 if (instance.samples == 0) return error.ExtentOverflowsIndexRange;
63 return .{
64 .{ .u32 = try runtimeExtentArgument(instance.count) },
65 .{ .u32 = instance.samples },
66 .{ .u32 = instance.seedLo() },
67 .{ .u32 = instance.seedHi() },
68 };
69 }
70 pub fn philoxFoldShapeProfileDimensions(instance: PhiloxFold) [2]artifact_product.KernelCallShapeProfileDimension {
71 return .{
72 .{ .name = instance.count_axis, .runtime_scalar_argument_index = 0, .bounds = randomRuntimeExtentBounds() },
73 .{ .name = "k", .runtime_scalar_argument_index = 1, .bounds = randomRuntimeExtentBounds() },
74 };
75 }
76 pub fn philoxFoldTuningExtents(instance: PhiloxFold) [2]u64 {
77 return .{ instance.count, instance.samples };
78 }
79
80 pub fn philoxFoldTuningOperation(instance: PhiloxFold) entry.Operation {
81 return .{ .random = .{ .philox_fold = instance.rounds } };
82 }
83
84 pub fn philoxFoldFamilyTuningKey(
85 backing_allocator: std.mem.Allocator,
86 device_fingerprint: u64,
87 instance: PhiloxFold,
88 ) !tuning.FamilyTuningKey {
89 const family_fingerprint = try philoxFoldFamilyFingerprint(backing_allocator, instance);
90 const extents = philoxFoldTuningExtents(instance);
91 return tuning.FamilyTuningKey.init(
92 device_fingerprint,
93 family_fingerprint,
94 entry.operationFingerprint(philoxFoldTuningOperation(instance)),
95 instance.dtype,
96 philox_fold_family_version,
97 extents[0..],
98 ) orelse unreachable;
99 }
100
101 pub fn philoxFoldFamilyFingerprint(backing_allocator: std.mem.Allocator, instance: PhiloxFold) !u64 {
102 var family = try randomShapeFamily(backing_allocator, "philox_fold", instance.count_axis);
103 defer family.deinit();
104 return shape.fingerprint(family);
105 }
106 pub fn createPhiloxFoldFamilyArtifact(
107 allocator: std.mem.Allocator,
108 handle: kernel.BackendHandle,
109 instance: PhiloxFold,
110 options: entry.ArtifactOptions,
111 ) !kernel.OwnedKernelCallArtifact {
112 const target = try philoxFoldFamilyTarget(allocator, instance);
113 defer allocator.free(target);
114 const entry_name = try philoxFoldFamilyEntryName(allocator, instance);
115 defer allocator.free(entry_name);
116 const family_fingerprint = options.shape_family_fingerprint orelse try philoxFoldFamilyFingerprint(allocator, instance);
117 const shape_profile_dimensions = philoxFoldShapeProfileDimensions(instance);
118 const shape_profile = options.shape_profile orelse artifact_product.KernelCallShapeProfile{
119 .name = "philox_fold",
120 .fingerprint = family_fingerprint,
121 .dimensions = shape_profile_dimensions[0..],
122 };
123
124 var graph = switch (instance.dtype) {
125 .f32 => try PhiloxFoldRuntimeFamilyF32.buildNamed(allocator, options.limits, entry_name, instance),
126 .i32 => try PhiloxFoldRuntimeFamilyI32.buildNamed(allocator, options.limits, entry_name, instance),
127 else => return error.UnsupportedDType,
128 };
129 defer graph.deinit();
130 return kernel.createKernelCallArtifact(allocator, handle, &graph, .{
131 .target = target,
132 .version = philox_fold_family_version,
133 .format = options.format,
134 .kernel_plan = options.kernel_plan,
135 .element_count_argument = options.element_count_argument,
136 .shape_family_fingerprint = family_fingerprint,
137 .shape_profile = shape_profile,
138 .launch = options.launch orelse try randomFoldDerivedLaunch(instance.threads),
139 .runtime_scalar_argument_count = if (options.runtime_scalar_argument_count == 0) 4 else options.runtime_scalar_argument_count,
140 .static_arguments = options.static_arguments,
141 });
142 }