tiny.accy.tensor.batch
Defined in tensor.
API (9)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/accy/src/tensor/batch.zig
zig
const std = @import("std");const emit = @import("emit.zig");const hook = @import("hook.zig");const interpret = @import("interpret/root.zig");const program_mod = @import("program.zig");const trace = @import("trace/root.zig");const transform = @import("transform.zig");const types = @import("type/root.zig");pub const Axis = union(enum) { none, axis: usize,};pub const Options = struct { axis_size: i64, in_axes: []const Axis, out_axis: usize = 0, axis_name: []const u8 = &.{},};const default_axis_name = "#batch";const BatchValue = struct { value: trace.Value, axis: ?usize,};pub fn mapped(axis_index: usize) Axis { return .{ .axis = axis_index };}pub fn vmap( allocator: std.mem.Allocator, source: *const program_mod.Program, options: Options,) !program_mod.Program { try validate(source, options); var builder = try trace.Builder.init(allocator, source.name); errdefer builder.deinit(); const resolved = try resolveAxisName(builder.arena.allocator(), source, options); const graph = interpret.Graph{ .builder = &builder }; return try interpret.run(allocator, source, semantics(graph, resolved));}pub fn vmapWith( allocator: std.mem.Allocator, source: *const program_mod.Program, options: Options, hooks: anytype,) !program_mod.Program { try validate(source, options); var builder = try trace.Builder.init(allocator, source.name); errdefer builder.deinit(); const resolved = try resolveAxisName(builder.arena.allocator(), source, options); const graph = interpret.Graph{ .builder = &builder }; return try interpret.run(allocator, source, semantics(hook.attach("batch", hooks, graph), resolved));}fn resolveAxisName(allocator: std.mem.Allocator, source: *const program_mod.Program, options: Options) !Options { var resolved = options; if (options.axis_name.len != 0) { try types.validateName(options.axis_name); return resolved; } var attempt: usize = 0; var name: []const u8 = default_axis_name; while (programHasAxis(source, name)) { attempt += 1; name = try std.fmt.allocPrint(allocator, "{s}{d}", .{ default_axis_name, attempt }); } resolved.axis_name = name; return resolved;}fn programHasAxis(source: *const program_mod.Program, name: []const u8) bool { for (source.values) |ty| { if (types.findDim(ty.dims, name) != null) return true; } for (source.operations) |op| { if (op.kind == .scan and subgraphHasAxis(op.kind.scan.body, name)) return true; } return false;}fn subgraphHasAxis(body: *const program_mod.Subgraph, name: []const u8) bool { for (body.values) |ty| { if (types.findDim(ty.dims, name) != null) return true; } for (body.operations) |op| { if (op.kind == .scan and subgraphHasAxis(op.kind.scan.body, name)) return true; } return false;}pub fn semantics(next: anytype, options: Options) Semantics(@TypeOf(next)) { return .{ .next = next, .options = options, };}pub fn semanticsWith(next: anytype, options: Options, hooks: anytype) Semantics(@TypeOf(hook.attach("batch", hooks, next))) { return semantics(hook.attach("batch", hooks, next), options);}pub fn Semantics(comptime Next: type) type { return struct { next: Next, options: Options, pub const Value: type = BatchValue; pub const Result: type = Next.Result; pub fn operation(self: *@This(), step: *interpret.Step(Value)) !Value { return batchOperation(self, step.op, step.values); } pub fn finish(self: *@This(), outputs: []const Value) !Result { const values = try self.builderHandle().arena.allocator().alloc(trace.Value, outputs.len); for (outputs, 0..) |output, index| { values[index] = if (output.axis) |axis| try moveBatchAxis(self, output.value, axis, self.options.out_axis) else try broadcastOutputAxis(self, output.value, self.options); } return self.next.finish(values); } pub fn builderHandle(self: *@This()) *trace.Builder { return self.next.builderHandle(); } pub fn axisName(self: *@This()) []const u8 { if (self.options.axis_name.len != 0) return self.options.axis_name; return default_axis_name; } };}fn batchDim(layer: anytype) types.Dim { return .{ .name = layer.axisName(), .extent = layer.options.axis_size };}fn batchOperation(layer: anytype, op: *const program_mod.Operation, map: []const BatchValue) !BatchValue { return switch (op.kind) { .parameter => |parameter| batchParameter(layer, op, parameter.index), .constant, .iota => .{ .value = try emitUnbatched(layer, op, map), .axis = null }, .unary => |unary| blk: { const input = map[unary.input.index]; break :blk .{ .value = try layer.next.bind(op, &.{input.value}), .axis = input.axis }; }, .binary => |binary| batchBinary(layer, op, map[binary.lhs.index], map[binary.rhs.index]), .reshape => |reshape| blk: { const input = map[reshape.input.index]; if (input.axis) |axis| { const allocator = layer.builderHandle().arena.allocator(); const dims = try types.insertDim(allocator, op.result.dims, axis, batchDim(layer)); var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = dims }; batched_op.kind = .{ .reshape = .{ .input = reshape.input, .new_shape = try types.extents(allocator, dims) } }; break :blk .{ .value = try layer.next.bind(&batched_op, &.{input.value}), .axis = axis }; } break :blk .{ .value = try emitUnbatched(layer, op, map), .axis = null }; }, .broadcast => |broadcast| batchBroadcast(layer, op, broadcast, map[broadcast.input.index]), .broadcast_in_dim => |broadcast| batchBroadcastInDim(layer, op, broadcast, map[broadcast.input.index]), .transpose => |transpose| batchTranspose(layer, op, transpose, map[transpose.input.index]), .reduce => |reduce| batchReduce(layer, op, reduce, map[reduce.input.index], map[reduce.init.index]), .gather => |gather| batchGather(layer, op, map[gather.input.index], map[gather.indices.index]), .scatter_add => |scatter_add| batchScatterAdd( layer, op, map[scatter_add.input.index], map[scatter_add.indices.index], map[scatter_add.updates.index], ), .sparse_cross_entropy => |sparse_cross_entropy| batchSparseCrossEntropy( layer, op, map[sparse_cross_entropy.logits.index], map[sparse_cross_entropy.targets.index], ), .dot_general => |dot| batchDot(layer, op, dot, map[dot.lhs.index], map[dot.rhs.index]), .compare => |compare| batchBinary(layer, op, map[compare.lhs.index], map[compare.rhs.index]), .select => |select| batchSelect(layer, op, map[select.pred.index], map[select.on_true.index], map[select.on_false.index]), .custom_call => error.CustomCallRequiresBatchContract, .scan => |scan| batchScan(layer, op, scan, map), .projection => |projection| blk: { const source = map[projection.source.index]; break :blk .{ .value = try layer.next.bind(op, &.{source.value}), .axis = source.axis }; }, };}fn batchScan(layer: anytype, op: *const program_mod.Operation, scan: program_mod.Scan, map: []const BatchValue) anyerror!BatchValue { var any_batched = false; for (scan.inits) |init_id| { if (map[init_id.index].axis != null) any_batched = true; } if (!any_batched) { var buffer: [program_mod.max_scan_carries]trace.Value = undefined; for (scan.inits, 0..) |init_id, index| buffer[index] = map[init_id.index].value; return .{ .value = try layer.next.bind(op, buffer[0..scan.inits.len]), .axis = null }; } const builder = layer.builderHandle(); var inits: [program_mod.max_scan_carries]trace.Value = undefined; for (scan.inits, 0..) |init_id, index| { const carried = map[init_id.index]; inits[index] = if (carried.axis) |axis| try moveBatchAxis(layer, carried.value, axis, 0) else try broadcastToBatchAxis(layer, carried.value, 0); } var body_program = program_mod.Program{ .arena = std.heap.ArenaAllocator.init(builder.allocator), .name = "scan_body", .values = scan.body.values, .operations = scan.body.operations, .parameters = scan.body.parameters, .outputs = scan.body.outputs, }; defer body_program.deinit(); var axes: [program_mod.max_scan_carries]Axis = undefined; for (0..scan.inits.len) |index| axes[index] = mapped(0); var batched_body = try vmap(builder.allocator, &body_program, .{ .axis_size = layer.options.axis_size, .in_axes = axes[0..scan.inits.len], .axis_name = layer.axisName(), }); defer batched_body.deinit(); const body_view = program_mod.Subgraph{ .values = batched_body.values, .operations = batched_body.operations, .parameters = batched_body.parameters, .outputs = batched_body.outputs, }; var batched_op = program_mod.Operation{ .id = program_mod.synthetic_id, .result = inits[0].ty, .kind = .{ .scan = .{ .length = scan.length, .inits = scan.inits, .body = &body_view } }, }; return .{ .value = try layer.next.bind(&batched_op, inits[0..scan.inits.len]), .axis = 0 };}fn batchGather(layer: anytype, op: *const program_mod.Operation, input: BatchValue, indices: BatchValue) !BatchValue { if (input.axis == null and indices.axis == null) { return .{ .value = try layer.next.bind(op, &.{ input.value, indices.value }), .axis = null }; } const gather = op.kind.gather; if (input.axis != null and indices.axis != null) { const moved_input = try moveBatchAxis(layer, input.value, input.axis.?, 0); const moved_indices = try moveBatchAxis(layer, indices.value, indices.axis.?, 0); return .{ .value = try pairedGather(layer, moved_input, moved_indices, try leadingAxis(gather.axis)), .axis = 0, }; } if (input.axis) |axis| { const moved_input = try moveBatchAxis(layer, input.value, axis, 0); return .{ .value = try emit.gather(layer, moved_input, indices.value, try leadingAxis(gather.axis)), .axis = 0, }; } const moved_indices = try moveBatchAxis(layer, indices.value, indices.axis.?, 0); return .{ .value = try emit.gather(layer, input.value, moved_indices, gather.axis), .axis = @intCast(gather.axis), };}fn batchScatterAdd(layer: anytype, op: *const program_mod.Operation, input: BatchValue, indices: BatchValue, updates: BatchValue) !BatchValue { if (input.axis == null and indices.axis == null and updates.axis == null) { return .{ .value = try layer.next.bind(op, &.{ input.value, indices.value, updates.value }), .axis = null }; } const scatter_add = op.kind.scatter_add; const moved_input = if (input.axis) |axis| try moveBatchAxis(layer, input.value, axis, 0) else try broadcastToBatchAxis(layer, input.value, 0); const moved_updates = if (updates.axis) |axis| try moveBatchAxis(layer, updates.value, axis, 0) else try broadcastToBatchAxis(layer, updates.value, 0); if (indices.axis) |axis| { const moved_indices = try moveBatchAxis(layer, indices.value, axis, 0); return .{ .value = try pairedScatterAdd(layer, moved_input, moved_indices, moved_updates, try leadingAxis(scatter_add.axis)), .axis = 0, }; } return .{ .value = try emit.scatterAdd(layer, moved_input, indices.value, moved_updates, try leadingAxis(scatter_add.axis)), .axis = 0, };}fn batchSparseCrossEntropy(layer: anytype, op: *const program_mod.Operation, logits: BatchValue, targets: BatchValue) !BatchValue { if (logits.axis == null and targets.axis == null) { return .{ .value = try layer.next.bind(op, &.{ logits.value, targets.value }), .axis = null }; } const sparse_cross_entropy = op.kind.sparse_cross_entropy; const moved_logits = if (logits.axis) |axis| try moveBatchAxis(layer, logits.value, axis, 0) else try broadcastToBatchAxis(layer, logits.value, 0); const moved_targets = if (targets.axis) |axis| try moveBatchAxis(layer, targets.value, axis, 0) else try broadcastToBatchAxis(layer, targets.value, 0); return .{ .value = try emit.sparseCrossEntropy(layer, moved_logits, moved_targets, try leadingAxis(sparse_cross_entropy.axis)), .axis = 0, };}fn pairedGather(layer: anytype, input: trace.Value, indices: trace.Value, axis: i64) !trace.Value { if (!input.ty.dtype.isNumeric()) return error.UnsupportedBatching; const axis_index = try batchIndexAxis(input, indices, axis); const allocator = layer.builderHandle().arena.allocator(); const index_dims = indices.ty.dims[1..]; const expanded_dims = try pairedExpandedDims(allocator, input.ty.dims, index_dims, axis_index); const source_positions = try emitIota(layer, .i32, expanded_dims, axis_index); const broadcasted_indices = try broadcastPairedIndices(layer, indices, expanded_dims, axis_index); const broadcasted_input = try broadcastPairedInput(layer, input, expanded_dims, axis_index, index_dims.len); const mask = try emit.compare(layer, .eq, source_positions, broadcasted_indices); const zero = try broadcastZero(layer, input.ty.dtype, expanded_dims); const selected = try emit.select(layer, mask, broadcasted_input, zero); const init = try emit.zeros(layer, trace.Type.scalar(input.ty.dtype)); return emit.reduce(layer, selected, init, .sum, &.{axis});}fn pairedScatterAdd(layer: anytype, input: trace.Value, indices: trace.Value, updates: trace.Value, axis: i64) !trace.Value { if (!input.ty.dtype.isNumeric()) return error.UnsupportedBatching; const axis_index = try batchIndexAxis(input, indices, axis); const allocator = layer.builderHandle().arena.allocator(); const index_dims = indices.ty.dims[1..]; const expanded_dims = try pairedExpandedDims(allocator, input.ty.dims, index_dims, axis_index); const source_positions = try emitIota(layer, .i32, expanded_dims, axis_index); const broadcasted_indices = try broadcastPairedIndices(layer, indices, expanded_dims, axis_index); const broadcasted_updates = try broadcastPairedUpdates(layer, updates, expanded_dims, axis_index); const mask = try emit.compare(layer, .eq, source_positions, broadcasted_indices); const zero = try broadcastZero(layer, input.ty.dtype, expanded_dims); const selected = try emit.select(layer, mask, broadcasted_updates, zero); const reduced = if (index_dims.len == 0) selected else blk: { const reduce_axes = try allocator.alloc(i64, index_dims.len); for (reduce_axes, 0..) |*slot, index| slot.* = @intCast(axis_index + 1 + index); const init = try emit.zeros(layer, trace.Type.scalar(input.ty.dtype)); break :blk try emit.reduce(layer, selected, init, .sum, reduce_axes); }; return emit.binary(layer, .add, input, reduced);}fn batchIndexAxis(input: trace.Value, indices: trace.Value, axis: i64) !usize { if (axis <= 0) return error.AxisOutOfRange; const axis_index = std.math.cast(usize, axis) orelse return error.AxisOutOfRange; if (axis_index >= input.ty.rank()) return error.AxisOutOfRange; if (indices.ty.rank() == 0) return error.AxisOutOfRange; if (indices.ty.dims[0].extent != input.ty.dims[0].extent) return error.ShapeMismatch; return axis_index;}fn pairedExpandedDims( allocator: std.mem.Allocator, input_dims: []const trace.Dim, index_dims: []const trace.Dim, axis: usize,) ![]const trace.Dim { const result = try allocator.alloc(trace.Dim, input_dims.len + index_dims.len); var out: usize = 0; for (input_dims[0 .. axis + 1]) |dim| { result[out] = dim; out += 1; } for (index_dims) |dim| { result[out] = dim; out += 1; } for (input_dims[axis + 1 ..]) |dim| { result[out] = dim; out += 1; } try types.validateDims(result); return result;}fn broadcastPairedInput(layer: anytype, input: trace.Value, expanded_dims: []const trace.Dim, axis: usize, index_rank: usize) !trace.Value { const allocator = layer.builderHandle().arena.allocator(); const mapping = try allocator.alloc(i64, input.ty.rank()); for (mapping, 0..) |*slot, index| { slot.* = if (index <= axis) @intCast(index) else @intCast(index + index_rank); } return emit.broadcastInDim(layer, input, expanded_dims, mapping);}fn broadcastPairedIndices(layer: anytype, indices: trace.Value, expanded_dims: []const trace.Dim, axis: usize) !trace.Value { const allocator = layer.builderHandle().arena.allocator(); const mapping = try allocator.alloc(i64, indices.ty.rank()); mapping[0] = 0; for (mapping[1..], 0..) |*slot, index| slot.* = @intCast(axis + 1 + index); return emit.broadcastInDim(layer, indices, expanded_dims, mapping);}fn broadcastPairedUpdates(layer: anytype, updates: trace.Value, expanded_dims: []const trace.Dim, axis: usize) !trace.Value { const allocator = layer.builderHandle().arena.allocator(); const mapping = try allocator.alloc(i64, updates.ty.rank()); for (mapping, 0..) |*slot, index| { slot.* = if (index < axis) @intCast(index) else @intCast(index + 1); } return emit.broadcastInDim(layer, updates, expanded_dims, mapping);}fn broadcastZero(layer: anytype, dtype: trace.DType, dims: []const trace.Dim) !trace.Value { const zero = try emit.zeros(layer, trace.Type.scalar(dtype)); return emit.broadcastInDim(layer, zero, dims, &.{});}fn emitIota(layer: anytype, dtype: trace.DType, dims: []const trace.Dim, axis: usize) !trace.Value { var op = program_mod.Operation{ .id = program_mod.synthetic_id, .result = .{ .dtype = dtype, .dims = dims }, .kind = .{ .iota = .{ .axis = @intCast(axis) } }, }; return layer.next.bind(&op, &.{});}fn leadingAxis(axis: i64) !i64 { if (axis < 0) return error.AxisOutOfRange; return std.math.add(i64, axis, 1) catch error.AxisOutOfRange;}fn batchParameter(layer: anytype, op: *const program_mod.Operation, index: usize) !BatchValue { return switch (layer.options.in_axes[index]) { .none => .{ .value = try layer.next.bind(op, &.{}), .axis = null }, .axis => |axis| blk: { var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = try types.insertDim(layer.builderHandle().arena.allocator(), op.result.dims, axis, batchDim(layer)), }; break :blk .{ .value = try layer.next.bind(&batched_op, &.{}), .axis = axis }; }, };}fn batchSelect( layer: anytype, op: *const program_mod.Operation, pred: BatchValue, on_true: BatchValue, on_false: BatchValue,) !BatchValue { const batch_axis = pred.axis orelse on_true.axis orelse on_false.axis orelse { return .{ .value = try layer.next.bind(op, &.{ pred.value, on_true.value, on_false.value }), .axis = null, }; }; const aligned_pred = try alignBatchOperand(layer, pred, batch_axis); const aligned_true = try alignBatchOperand(layer, on_true, batch_axis); const aligned_false = try alignBatchOperand(layer, on_false, batch_axis); return .{ .value = try layer.next.bind(op, &.{ aligned_pred, aligned_true, aligned_false }), .axis = batch_axis, };}fn alignBatchOperand(layer: anytype, operand: BatchValue, batch_axis: usize) !trace.Value { if (operand.axis) |axis| { if (axis == batch_axis) return operand.value; return moveBatchAxis(layer, operand.value, axis, batch_axis); } return broadcastToBatchAxis(layer, operand.value, batch_axis);}fn broadcastToBatchAxis(layer: anytype, value: trace.Value, batch_axis: usize) !trace.Value { const allocator = layer.builderHandle().arena.allocator(); const result_dims = try types.insertDim(allocator, value.ty.dims, batch_axis, batchDim(layer)); const dims = try allocator.alloc(i64, value.ty.dims.len); var dim_index: usize = 0; for (0..result_dims.len) |axis| { if (axis == batch_axis) continue; dims[dim_index] = @intCast(axis); dim_index += 1; } var batched_op = program_mod.Operation{ .id = program_mod.synthetic_id, .result = .{ .dtype = value.ty.dtype, .dims = result_dims }, .kind = .{ .broadcast_in_dim = .{ .input = program_mod.synthetic_id, .broadcast_dims = dims } }, }; return layer.next.bind(&batched_op, &.{value});}fn batchBinary(layer: anytype, op: *const program_mod.Operation, lhs: BatchValue, rhs: BatchValue) !BatchValue { if (lhs.axis == null and rhs.axis == null) { return .{ .value = try layer.next.bind(op, &.{ lhs.value, rhs.value }), .axis = null }; } if (lhs.axis) |lhs_axis| { if (rhs.axis) |rhs_axis| { const aligned_rhs = if (rhs_axis == lhs_axis) rhs.value else try moveBatchAxis(layer, rhs.value, rhs_axis, lhs_axis); return .{ .value = try layer.next.bind(op, &.{ lhs.value, aligned_rhs }), .axis = lhs_axis }; } const lifted_rhs = try broadcastToBatchAxis(layer, rhs.value, lhs_axis); return .{ .value = try layer.next.bind(op, &.{ lhs.value, lifted_rhs }), .axis = lhs_axis }; } const rhs_axis = rhs.axis.?; const lifted_lhs = try broadcastToBatchAxis(layer, lhs.value, rhs_axis); return .{ .value = try layer.next.bind(op, &.{ lifted_lhs, rhs.value }), .axis = rhs_axis };}fn batchBroadcast( layer: anytype, op: *const program_mod.Operation, broadcast: program_mod.Broadcast, input: BatchValue,) !BatchValue { if (input.axis == null) return .{ .value = try layer.next.bind(op, &.{input.value}), .axis = null }; const moved = try moveBatchAxis(layer, input.value, input.axis.?, 0); if (input.value.ty.rank() != 1) return error.UnsupportedBatching; const result_dims = try types.insertDim(layer.builderHandle().arena.allocator(), op.result.dims, 0, batchDim(layer)); var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = result_dims }; batched_op.kind = .{ .broadcast_in_dim = .{ .input = broadcast.input, .broadcast_dims = &.{0} } }; return .{ .value = try layer.next.bind(&batched_op, &.{moved}), .axis = 0 };}fn batchBroadcastInDim( layer: anytype, op: *const program_mod.Operation, broadcast: program_mod.BroadcastInDim, input: BatchValue,) !BatchValue { if (input.axis == null) { return .{ .value = try layer.next.bind(op, &.{input.value}), .axis = null }; } const moved = try moveBatchAxis(layer, input.value, input.axis.?, 0); const result_dims = try types.insertDim(layer.builderHandle().arena.allocator(), op.result.dims, 0, batchDim(layer)); const dims = try shiftAxesWithLeading(layer.builderHandle().arena.allocator(), broadcast.broadcast_dims); var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = result_dims }; batched_op.kind = .{ .broadcast_in_dim = .{ .input = broadcast.input, .broadcast_dims = dims } }; return .{ .value = try layer.next.bind(&batched_op, &.{moved}), .axis = 0 };}fn batchTranspose( layer: anytype, op: *const program_mod.Operation, transpose: program_mod.Transpose, input: BatchValue,) !BatchValue { if (input.axis == null) return .{ .value = try layer.next.bind(op, &.{input.value}), .axis = null }; const allocator = layer.builderHandle().arena.allocator(); const moved = try moveBatchAxis(layer, input.value, input.axis.?, 0); const permutation = try shiftAxesWithLeading(allocator, transpose.permutation); var batched_op = op.*; batched_op.result = .{ .dtype = input.value.ty.dtype, .dims = try types.permuted(allocator, moved.ty.dims, permutation) }; batched_op.kind = .{ .transpose = .{ .input = transpose.input, .permutation = permutation } }; return .{ .value = try layer.next.bind(&batched_op, &.{moved}), .axis = 0 };}fn batchReduce( layer: anytype, op: *const program_mod.Operation, reduce: program_mod.Reduce, input: BatchValue, init: BatchValue,) !BatchValue { if (input.axis == null and init.axis == null) { return .{ .value = try layer.next.bind(op, &.{ input.value, init.value }), .axis = null }; } if (init.axis != null) return error.UnsupportedBatching; if (input.axis == null) return error.UnsupportedBatching; const allocator = layer.builderHandle().arena.allocator(); const moved = try moveBatchAxis(layer, input.value, input.axis.?, 0); const dimensions = try shiftAxes(allocator, reduce.dimensions, 1); var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = try types.removeAxes(allocator, moved.ty.dims, dimensions), }; batched_op.kind = .{ .reduce = .{ .input = reduce.input, .init = reduce.init, .reducer = reduce.reducer, .dimensions = dimensions } }; return .{ .value = try layer.next.bind(&batched_op, &.{ moved, init.value }), .axis = 0 };}fn batchDot( layer: anytype, op: *const program_mod.Operation, dot: program_mod.DotGeneral, lhs: BatchValue, rhs: BatchValue,) !BatchValue { if (lhs.axis == null and rhs.axis == null) { return .{ .value = try layer.next.bind(op, &.{ lhs.value, rhs.value }), .axis = null }; } const allocator = layer.builderHandle().arena.allocator(); const lhs_value = if (lhs.axis) |axis| try moveBatchAxis(layer, lhs.value, axis, 0) else try broadcastToBatchAxis(layer, lhs.value, 0); const rhs_value = if (rhs.axis) |axis| try moveBatchAxis(layer, rhs.value, axis, 0) else try broadcastToBatchAxis(layer, rhs.value, 0); const lhs_contract = try shiftAxes(allocator, dot.lhs_contract, 1); const rhs_contract = try shiftAxes(allocator, dot.rhs_contract, 1); const lhs_batch = try shiftAxesWithLeading(allocator, dot.lhs_batch); const rhs_batch = try shiftAxesWithLeading(allocator, dot.rhs_batch); const result_dims = try types.dotGeneralDims( allocator, lhs_value.ty.dims, rhs_value.ty.dims, lhs_contract, rhs_contract, lhs_batch, rhs_batch, ); var batched_op = op.*; batched_op.result = .{ .dtype = op.result.dtype, .dims = result_dims }; batched_op.kind = .{ .dot_general = .{ .lhs = dot.lhs, .rhs = dot.rhs, .lhs_contract = lhs_contract, .rhs_contract = rhs_contract, .lhs_batch = lhs_batch, .rhs_batch = rhs_batch, }, }; return .{ .value = try layer.next.bind(&batched_op, &.{ lhs_value, rhs_value }), .axis = 0 };}fn emitUnbatched(layer: anytype, op: *const program_mod.Operation, map: []const BatchValue) !trace.Value { return switch (op.kind) { .parameter, .constant, .iota => layer.next.bind(op, &.{}), .unary => |unary| layer.next.bind(op, &.{map[unary.input.index].value}), .binary => |binary| layer.next.bind(op, &.{ map[binary.lhs.index].value, map[binary.rhs.index].value }), .broadcast => |broadcast| layer.next.bind(op, &.{map[broadcast.input.index].value}), .broadcast_in_dim => |broadcast| layer.next.bind(op, &.{map[broadcast.input.index].value}), .reshape => |reshape| layer.next.bind(op, &.{map[reshape.input.index].value}), .transpose => |transpose| layer.next.bind(op, &.{map[transpose.input.index].value}), .reduce => |reduce| layer.next.bind(op, &.{ map[reduce.input.index].value, map[reduce.init.index].value }), .gather => |gather| layer.next.bind(op, &.{ map[gather.input.index].value, map[gather.indices.index].value }), .scatter_add => |scatter_add| layer.next.bind(op, &.{ map[scatter_add.input.index].value, map[scatter_add.indices.index].value, map[scatter_add.updates.index].value, }), .sparse_cross_entropy => |sparse_cross_entropy| layer.next.bind(op, &.{ map[sparse_cross_entropy.logits.index].value, map[sparse_cross_entropy.targets.index].value, }), .compare => |compare| layer.next.bind(op, &.{ map[compare.lhs.index].value, map[compare.rhs.index].value }), .select => |select| layer.next.bind(op, &.{ map[select.pred.index].value, map[select.on_true.index].value, map[select.on_false.index].value }), .custom_call => error.CustomCallRequiresBatchContract, .dot_general => |dot| layer.next.bind(op, &.{ map[dot.lhs.index].value, map[dot.rhs.index].value }), .scan, .projection => error.ScanBatchingUnsupported, };}fn broadcastOutputAxis(layer: anytype, value: trace.Value, options: Options) !trace.Value { return broadcastToBatchAxis(layer, value, options.out_axis);}fn moveBatchAxis(layer: anytype, value: trace.Value, from: usize, to: usize) !trace.Value { if (from == to) return value; const rank = value.ty.rank(); if (from >= rank or to >= rank) return error.AxisOutOfRange; const permutation = try layer.builderHandle().arena.allocator().alloc(i64, rank); var remaining: usize = 0; for (permutation, 0..) |*axis, index| { if (index == to) { axis.* = @intCast(from); } else { while (remaining == from) remaining += 1; axis.* = @intCast(remaining); remaining += 1; } } return emit.transpose(layer, value, permutation);}fn shiftAxesWithLeading(allocator: std.mem.Allocator, axes: []const i64) ![]const i64 { const result = try allocator.alloc(i64, axes.len + 1); result[0] = 0; for (axes, 0..) |axis, index| { result[index + 1] = axis + 1; } return result;}fn shiftAxes(allocator: std.mem.Allocator, axes: []const i64, amount: i64) ![]const i64 { const result = try allocator.alloc(i64, axes.len); for (axes, 0..) |axis, index| { result[index] = axis + amount; } return result;}pub fn validate(source: *const program_mod.Program, options: Options) !void { if (options.axis_size < 0) return error.InvalidDimension; if (options.in_axes.len != source.parameters.len) return error.InAxisCountMismatch; for (source.parameters) |id| { const parameter = source.operation(id).kind.parameter; switch (options.in_axes[parameter.index]) { .none => {}, .axis => |axis| if (axis > source.typeOf(id).rank()) return error.AxisOutOfRange, } }}fn elementwiseBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return try (try args[0].mul(args[1])).tanh();}fn expectExtents(expected: []const i64, ty: trace.Type) !void { try std.testing.expectEqual(expected.len, ty.dims.len); for (expected, ty.dims) |extent, dim| { try std.testing.expectEqual(extent, dim.extent); }}test "tensor vmap batches elementwise programs with a generated axis name" { var source = try trace.define(std.testing.allocator, "vmap_elementwise", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, elementwiseBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), mapped(0) }, }); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 2), batched.parameters.len); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name); try std.testing.expectEqualStrings("lane", batched.typeOf(batched.outputs[0]).dims[1].name);}test "tensor vmap names the batch axis on request" { var source = try trace.define(std.testing.allocator, "vmap_named_axis", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, elementwiseBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), mapped(0) }, .axis_name = "walk", }); defer batched.deinit(); try std.testing.expectEqualStrings("walk", batched.typeOf(batched.outputs[0]).dims[0].name);}test "tensor vmap generates fresh names for nested batching" { var source = try trace.define(std.testing.allocator, "vmap_nested", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, elementwiseBody); defer source.deinit(); var once = try vmap(std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), mapped(0) }, }); defer once.deinit(); var twice = try vmap(std.testing.allocator, &once, .{ .axis_size = 3, .in_axes = &.{ mapped(0), mapped(0) }, }); defer twice.deinit(); const out = twice.typeOf(twice.outputs[0]); try expectExtents(&.{ 3, 8, 4 }, out); try std.testing.expectEqualStrings("#batch1", out.dims[0].name); try std.testing.expectEqualStrings("#batch", out.dims[1].name);}fn unmappedOutputBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return args[1];}test "tensor vmap broadcasts unmapped outputs" { var source = try trace.define(std.testing.allocator, "vmap_unmapped_output", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, unmappedOutputBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), .none }, }); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 2), batched.parameters.len); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.outputs[0]));}const GeneratedCounts = struct { broadcast_in_dim: usize = 0, transpose: usize = 0,};const GeneratedCounter = struct { counts: *GeneratedCounts, pub fn broadcastInDim(self: *@This(), ctx: anytype) !trace.Value { self.counts.broadcast_in_dim += 1; return ctx.default(); } pub fn transpose(self: *@This(), ctx: anytype) !trace.Value { self.counts.transpose += 1; return ctx.default(); }};test "tensor vmap binds generated output broadcasts through downstream semantics" { var source = try trace.define(std.testing.allocator, "vmap_generated_broadcast", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, unmappedOutputBody); defer source.deinit(); var builder = try trace.Builder.init(std.testing.allocator, source.name); errdefer builder.deinit(); var counts: GeneratedCounts = .{}; const graph = interpret.Graph{ .builder = &builder }; const counted = interpret.bind(GeneratedCounter{ .counts = &counts }).attach(graph); var batched = try interpret.run(std.testing.allocator, &source, semantics(counted, .{ .axis_size = 8, .in_axes = &.{ mapped(0), .none }, })); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 1), counts.broadcast_in_dim); try std.testing.expectEqual(@as(usize, 0), counts.transpose); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.outputs[0]));}test "tensor vmapWith routes batch generated ops through user semantics" { var source = try trace.define(std.testing.allocator, "vmap_with_generated_broadcast", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, unmappedOutputBody); defer source.deinit(); var counts: GeneratedCounts = .{}; var batched = try vmapWith( std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), .none }, }, .{ .batch = interpret.bind(GeneratedCounter{ .counts = &counts }) }, ); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 1), counts.broadcast_in_dim); try std.testing.expectEqual(@as(usize, 0), counts.transpose); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.outputs[0]));}const GeneratedBroadcastRewrite = struct { seen: *usize, pub fn broadcastInDim(self: *@This(), ctx: *transform.Context) !?trace.Value { self.seen.* += 1; try std.testing.expect(!ctx.isZero(0)); try std.testing.expect(ctx.constantPayload(0) == null); return null; }};test "tensor vmap generated ops are safe for downstream rewrite metadata queries" { var source = try trace.define(std.testing.allocator, "vmap_generated_rewrite_metadata", &.{ types.spec(.f32, .{ .lane = 4 }), types.spec(.f32, .{ .lane = 4 }), }, unmappedOutputBody); defer source.deinit(); var builder = try trace.Builder.init(std.testing.allocator, source.name); errdefer builder.deinit(); var seen: usize = 0; const graph = interpret.Graph{ .builder = &builder }; const rewrite = transform.semantics(&source, graph, GeneratedBroadcastRewrite{ .seen = &seen }); var batched = try interpret.run(std.testing.allocator, &source, semantics(rewrite, .{ .axis_size = 8, .in_axes = &.{ mapped(0), .none }, })); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 1), seen); try expectExtents(&.{ 8, 4 }, batched.typeOf(batched.outputs[0]));}fn gatherBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return args[0].gather(args[1], .vocab);}test "tensor vmap gathers with batched indices" { var source = try trace.define(std.testing.allocator, "vmap_gather_indices", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), }, gatherBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ .none, mapped(0) }, }); defer batched.deinit(); try expectExtents(&.{ 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{ 7, 5 }, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name); try std.testing.expectEqualStrings("token", batched.typeOf(batched.outputs[0]).dims[1].name);}test "tensor vmap gathers with a batched input" { var source = try trace.define(std.testing.allocator, "vmap_gather_input", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), }, gatherBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ mapped(0), .none }, }); defer batched.deinit(); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{5}, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name); try std.testing.expectEqualStrings("token", batched.typeOf(batched.outputs[0]).dims[1].name);}test "tensor vmap gathers with shared batched input and indices" { var source = try trace.define(std.testing.allocator, "vmap_gather_shared_batch", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), }, gatherBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ mapped(0), mapped(0) }, }); defer batched.deinit(); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{ 7, 5 }, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name); try std.testing.expectEqualStrings("token", batched.typeOf(batched.outputs[0]).dims[1].name);}fn scatterAddBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return args[0].scatterAdd(args[1], args[2], .vocab);}test "tensor vmap scatters with batched updates and shared indices" { var source = try trace.define(std.testing.allocator, "vmap_scatter_add_updates", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), types.spec(.f32, .{ .token = 5, .channel = 8 }), }, scatterAddBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ mapped(0), .none, mapped(0) }, }); defer batched.deinit(); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{5}, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.parameters[2])); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name);}test "tensor vmap scatters with shared batched indices" { var source = try trace.define(std.testing.allocator, "vmap_scatter_add_shared_indices", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), types.spec(.f32, .{ .token = 5, .channel = 8 }), }, scatterAddBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ mapped(0), mapped(0), mapped(0) }, }); defer batched.deinit(); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{ 7, 5 }, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.parameters[2])); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name);}test "tensor vmap scatters with batched indices and shared input" { var source = try trace.define(std.testing.allocator, "vmap_scatter_add_batched_indices_shared_input", &.{ types.spec(.f32, .{ .vocab = 32, .channel = 8 }), types.spec(.i32, .{ .token = 5 }), types.spec(.f32, .{ .token = 5, .channel = 8 }), }, scatterAddBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 7, .in_axes = &.{ .none, mapped(0), mapped(0) }, }); defer batched.deinit(); try expectExtents(&.{ 32, 8 }, batched.typeOf(batched.parameters[0])); try expectExtents(&.{ 7, 5 }, batched.typeOf(batched.parameters[1])); try expectExtents(&.{ 7, 5, 8 }, batched.typeOf(batched.parameters[2])); try expectExtents(&.{ 7, 32, 8 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name);}fn identityBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return args[0];}test "tensor vmap binds generated batch-axis moves through downstream semantics" { var source = try trace.define(std.testing.allocator, "vmap_generated_transpose", &.{ types.spec(.f32, .{ .row = 2, .col = 4 }), }, identityBody); defer source.deinit(); var builder = try trace.Builder.init(std.testing.allocator, source.name); errdefer builder.deinit(); var counts: GeneratedCounts = .{}; const graph = interpret.Graph{ .builder = &builder }; const counted = interpret.bind(GeneratedCounter{ .counts = &counts }).attach(graph); var batched = try interpret.run(std.testing.allocator, &source, semantics(counted, .{ .axis_size = 8, .in_axes = &.{mapped(1)}, .out_axis = 0, })); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 0), counts.broadcast_in_dim); try std.testing.expectEqual(@as(usize, 1), counts.transpose); try expectExtents(&.{ 8, 2, 4 }, batched.typeOf(batched.outputs[0]));}fn contractBody(_: *trace.Builder, args: []const trace.Value) !trace.Value { return try args[0].contract(args[1], .k);}test "tensor vmap batches contraction with an unmapped rhs" { var source = try trace.define(std.testing.allocator, "vmap_contract", &.{ types.spec(.f32, .{ .m = 2, .k = 4 }), types.spec(.f32, .{ .k = 4, .n = 3 }), }, contractBody); defer source.deinit(); var batched = try vmap(std.testing.allocator, &source, .{ .axis_size = 8, .in_axes = &.{ mapped(0), .none }, }); defer batched.deinit(); try std.testing.expectEqual(@as(usize, 2), batched.parameters.len); try expectExtents(&.{ 8, 2, 3 }, batched.typeOf(batched.outputs[0])); try std.testing.expectEqualStrings("#batch", batched.typeOf(batched.outputs[0]).dims[0].name);}fn addZeroBody(builder: *trace.Builder, args: []const trace.Value) !trace.Value { const zero = try builder.full(.f32, .{ .lane = 4 }, 0.0); return try args[0].add(zero);}const BatchDropAddZero = struct { pub fn add(_: *@This(), ctx: *transform.Context) !?trace.Value { if (ctx.isZero(1)) return ctx.arg(0); if (ctx.isZero(0)) return ctx.arg(1); return null; }};test "tensor vmap can delegate primitive emission through rewrite semantics" { var source = try trace.define(std.testing.allocator, "vmap_rewrite", &.{ types.spec(.f32, .{ .lane = 4 }), }, addZeroBody); defer source.deinit(); const options = Options{ .axis_size = 8, .in_axes = &.{mapped(0)}, }; var plain = try vmap(std.testing.allocator, &source, options); defer plain.deinit(); var builder = try trace.Builder.init(std.testing.allocator, source.name); errdefer builder.deinit(); const graph = interpret.Graph{ .builder = &builder }; const rewrite = transform.semantics(&source, graph, BatchDropAddZero{}); var rewritten = try interpret.run(std.testing.allocator, &source, semantics(rewrite, options)); defer rewritten.deinit(); try std.testing.expect(rewritten.operationCount() < plain.operationCount()); try std.testing.expectEqual(@as(u32, 0), rewritten.outputs[0].index); try expectExtents(&.{ 8, 4 }, rewritten.typeOf(rewritten.outputs[0]));}Source: lib/accy/src/tensor/root.zig:9
zig
pub const batch = @import("batch.zig");Complete caller list for tensor.batch.mapped
19 direct callers.
lib.accy.src.tensor.batch.batchScan[function] — private source atlib/accy/src/tensor/batch.zig:200in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmapWith_routes_batch_generated_ops_through_user_semantics[function] — test source atlib/accy/src/tensor/batch.zig:873in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_batches_contraction_with_an_unmapped_rhs[function] — test source atlib/accy/src/tensor/batch.zig:1094in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_batches_elementwise_programs_with_a_generated_axis_name[function] — test source atlib/accy/src/tensor/batch.zig:747in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_binds_generated_batch-axis_moves_through_downstream_semantics[function] — test source atlib/accy/src/tensor/batch.zig:1066in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_binds_generated_output_broadcasts_through_downstream_semantics[function] — test source atlib/accy/src/tensor/batch.zig:849in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_broadcasts_unmapped_outputs[function] — test source atlib/accy/src/tensor/batch.zig:813in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_can_delegate_primitive_emission_through_rewrite_semantics[function] — test source atlib/accy/src/tensor/batch.zig:1125in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_a_batched_input[function] — test source atlib/accy/src/tensor/batch.zig:955in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_batched_indices[function] — test source atlib/accy/src/tensor/batch.zig:935in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_shared_batched_input_and_indices[function] — test source atlib/accy/src/tensor/batch.zig:975in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_generated_ops_are_safe_for_downstream_rewrite_metadata_queries[function] — test source atlib/accy/src/tensor/batch.zig:908in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_generates_fresh_names_for_nested_batching[function] — test source atlib/accy/src/tensor/batch.zig:784in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_names_the_batch_axis_on_request[function] — test source atlib/accy/src/tensor/batch.zig:767in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_batched_indices_and_shared_input[function] — test source atlib/accy/src/tensor/batch.zig:1041in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_batched_updates_and_shared_indices[function] — test source atlib/accy/src/tensor/batch.zig:999in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_shared_batched_indices[function] — test source atlib/accy/src/tensor/batch.zig:1020in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.function.test_tensor_function_composes_per-example_gradients_through_vmap_of_grad[function] — test source atlib/accy/src/tensor/function.zig:162in nearest public ownertiny.accy.tensor.functionlib.accy.src.tensor.function.test_tensor_function_owns_every_derived_graph_through_one_deinit[function] — test source atlib/accy/src/tensor/function.zig:87in nearest public ownertiny.accy.tensor.function
Complete caller list for tensor.batch.semantics
7 direct callers.
tiny.accy.tensor.batch.semanticsWith[function] atlib/accy/src/tensor/batch.zig:107lib.accy.src.tensor.batch.test_tensor_vmap_binds_generated_batch-axis_moves_through_downstream_semantics[function] — test source atlib/accy/src/tensor/batch.zig:1066in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_binds_generated_output_broadcasts_through_downstream_semantics[function] — test source atlib/accy/src/tensor/batch.zig:849in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_can_delegate_primitive_emission_through_rewrite_semantics[function] — test source atlib/accy/src/tensor/batch.zig:1125in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_generated_ops_are_safe_for_downstream_rewrite_metadata_queries[function] — test source atlib/accy/src/tensor/batch.zig:908in nearest public ownertiny.accy.tensor.batchtiny.accy.tensor.batch.vmap[function] atlib/accy/src/tensor/batch.zig:33tiny.accy.tensor.batch.vmapWith[function] atlib/accy/src/tensor/batch.zig:48
Complete caller list for tensor.batch.vmap
24 direct callers.
lib.accy.src.tensor.batch.batchScan[function] — private source atlib/accy/src/tensor/batch.zig:200in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_batches_contraction_with_an_unmapped_rhs[function] — test source atlib/accy/src/tensor/batch.zig:1094in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_batches_elementwise_programs_with_a_generated_axis_name[function] — test source atlib/accy/src/tensor/batch.zig:747in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_broadcasts_unmapped_outputs[function] — test source atlib/accy/src/tensor/batch.zig:813in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_can_delegate_primitive_emission_through_rewrite_semantics[function] — test source atlib/accy/src/tensor/batch.zig:1125in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_a_batched_input[function] — test source atlib/accy/src/tensor/batch.zig:955in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_batched_indices[function] — test source atlib/accy/src/tensor/batch.zig:935in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_gathers_with_shared_batched_input_and_indices[function] — test source atlib/accy/src/tensor/batch.zig:975in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_generates_fresh_names_for_nested_batching[function] — test source atlib/accy/src/tensor/batch.zig:784in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_names_the_batch_axis_on_request[function] — test source atlib/accy/src/tensor/batch.zig:767in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_batched_indices_and_shared_input[function] — test source atlib/accy/src/tensor/batch.zig:1041in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_batched_updates_and_shared_indices[function] — test source atlib/accy/src/tensor/batch.zig:999in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.batch.test_tensor_vmap_scatters_with_shared_batched_indices[function] — test source atlib/accy/src/tensor/batch.zig:1020in nearest public ownertiny.accy.tensor.batchlib.accy.src.tensor.dsl.surface.root.Derived[function] — private source atlib/accy/src/tensor/dsl/surface/root.zig:69in nearest public ownerlib.accy.src.tensor.dsl.surface.roottiny.accy.tensor.Function.vmap[method] atlib/accy/src/tensor/function.zig:49lib.accy.src.tensor.test.test_accy_tensor_grad_composes_with_vmap_and_lowering[function] — test source atlib/accy/src/tensor/test.zig:230in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_pullback_composes_with_vmap_and_lowering[function] — test source atlib/accy/src/tensor/test.zig:196in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_all_batched_scatter_add_accumulates_duplicate_indices_on_cpu[function] — test source atlib/accy/src/tensor/test.zig:474in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_batched_index_scatter_add_broadcasts_the_shared_input_on_cpu[function] — test source atlib/accy/src/tensor/test.zig:533in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_batches_scan_structurally[function] — test source atlib/accy/src/tensor/test.zig:971in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_composes_with_dense_grad_and_lowering[function] — test source atlib/accy/src/tensor/test.zig:1063in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_composes_with_linearize_and_lowering[function] — test source atlib/accy/src/tensor/test.zig:170in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_shared_batch_gather_matches_the_host_reference_on_cpu[function] — test source atlib/accy/src/tensor/test.zig:423in nearest public ownerlib.accy.src.tensor.testlib.accy.src.tensor.test.test_accy_tensor_vmap_sparse_cross_entropy_matches_the_host_reference_on_cpu[function] — test source atlib/accy/src/tensor/test.zig:682in nearest public ownerlib.accy.src.tensor.test
Audit
| Definitions | 10 |
|---|---|
| Public names | 15 |
| Members | 6 |
| Version | 26.7.0 |
| Revision | daab053ee433 |