tiny.pluck.definition_order
Defined in tiny.pluck.
API (6)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/pluck/src/order.zig
zig
const std = @import("std");const pexpr = @import("pexpr.zig");const Allocator = std.mem.Allocator;const PExpr = pexpr.PExpr;const Definitions = pexpr.Definitions;const Symbol = pexpr.Symbol;pub const DefinitionOrderMode = enum { none, topological, min_fill,};pub const DefinitionOrder = struct { map: std.StringHashMapUnmanaged(i32) = .{}, pub fn getIndex(self: *const DefinitionOrder, name: Symbol) ?i32 { return self.map.get(name); } pub fn deinit(self: *DefinitionOrder, allocator: Allocator) void { self.map.deinit(allocator); }};pub fn modeToString(mode: DefinitionOrderMode) []const u8 { return switch (mode) { .none => "none", .topological => "topological", .min_fill => "min-fill", };}pub fn buildDefinitionOrder( allocator: Allocator, definitions: *const Definitions, mode: DefinitionOrderMode,) !?*DefinitionOrder { if (mode == .none) return null; const def_names = try collectUserDefNamesSorted(allocator, definitions); defer allocator.free(def_names); if (def_names.len == 0) return null; const ordered = switch (mode) { .topological => try topologicalSortDefs(allocator, definitions, def_names), .min_fill => try minFillOrderDefs(allocator, definitions, def_names), .none => unreachable, }; defer allocator.free(ordered); const order = try allocator.create(DefinitionOrder); order.* = DefinitionOrder{}; for (ordered, 0..) |name, idx| { try order.map.put(allocator, name, @intCast(idx)); } return order;}const UserDefinitionNameOrder = struct { fn lessThan(_: void, a: Symbol, b: Symbol) bool { return std.mem.lessThan(u8, a, b); }};fn collectUserDefNamesSorted(allocator: Allocator, definitions: *const Definitions) ![]Symbol { var names: std.ArrayList(Symbol) = .empty; defer names.deinit(allocator); var iter = definitions.defs.iterator(); while (iter.next()) |entry| { if (entry.value_ptr.is_stdlib) continue; try names.append(allocator, entry.value_ptr.name); } const slice = try names.toOwnedSlice(allocator); std.sort.heap(Symbol, slice, {}, UserDefinitionNameOrder.lessThan); return slice;}fn topologicalSortDefs( allocator: Allocator, definitions: *const Definitions, def_names: []const Symbol,) ![]Symbol { const n = def_names.len; if (n == 0) return &[_]Symbol{}; var edges = try allocator.alloc(std.ArrayList(usize), n); defer { for (edges) |*e| e.deinit(allocator); allocator.free(edges); } for (edges) |*e| e.* = .empty; var name_to_idx = std.StringHashMap(usize).init(allocator); defer name_to_idx.deinit(); for (def_names, 0..) |name, i| { try name_to_idx.put(name, i); } for (def_names, 0..) |name, i| { const def = definitions.defs.get(name) orelse continue; var deps: std.ArrayList(usize) = .empty; defer deps.deinit(allocator); collectDefinedRefs(def.expr, &name_to_idx, &deps, allocator); for (deps.items) |dep_idx| { try edges[dep_idx].append(allocator, i); } } var in_degree = try allocator.alloc(usize, n); defer allocator.free(in_degree); @memset(in_degree, 0); for (edges) |e| { for (e.items) |dep_idx| { in_degree[dep_idx] += 1; } } var queue: std.ArrayList(usize) = .empty; defer queue.deinit(allocator); for (in_degree, 0..) |deg, i| { if (deg == 0) try queue.append(allocator, i); } var result = try allocator.alloc(Symbol, n); var result_idx: usize = 0; while (queue.items.len > 0) { const idx = queue.orderedRemove(0); result[result_idx] = def_names[idx]; result_idx += 1; for (edges[idx].items) |dep_idx| { in_degree[dep_idx] -= 1; if (in_degree[dep_idx] == 0) { try queue.append(allocator, dep_idx); } } } if (result_idx < n) { @memcpy(result, def_names); } return result;}fn minFillOrderDefs( allocator: Allocator, definitions: *const Definitions, def_names: []const Symbol,) ![]Symbol { const n = def_names.len; if (n == 0) return &[_]Symbol{}; const adj_size = std.math.mul(usize, n, n) catch return error.OutOfMemory; var adj = try allocator.alloc(u8, adj_size); defer allocator.free(adj); @memset(adj, 0); var name_to_idx = std.StringHashMap(usize).init(allocator); defer name_to_idx.deinit(); for (def_names, 0..) |name, i| { try name_to_idx.put(name, i); } for (def_names, 0..) |name, i| { const def = definitions.defs.get(name) orelse continue; var deps: std.ArrayList(usize) = .empty; defer deps.deinit(allocator); collectDefinedRefs(def.expr, &name_to_idx, &deps, allocator); for (deps.items) |dep_idx| { adj[i * n + dep_idx] = 1; adj[dep_idx * n + i] = 1; } for (deps.items, 0..) |dep_i, idx_i| { for (deps.items[idx_i + 1 ..]) |dep_j| { adj[dep_i * n + dep_j] = 1; adj[dep_j * n + dep_i] = 1; } } } var eliminated = try allocator.alloc(bool, n); defer allocator.free(eliminated); @memset(eliminated, false); var neighbors_buf = try allocator.alloc(usize, n); defer allocator.free(neighbors_buf); var order = try allocator.alloc(Symbol, n); var out_idx: usize = 0; while (out_idx < n) : (out_idx += 1) { var best_var: ?usize = null; var best_fill: usize = std.math.maxInt(usize); var best_degree: usize = std.math.maxInt(usize); for (0..n) |v| { if (eliminated[v]) continue; var degree: usize = 0; for (0..n) |u| { if (u == v or eliminated[u]) continue; if (adj[v * n + u] != 0) { neighbors_buf[degree] = u; degree += 1; } } var fill: usize = 0; for (neighbors_buf[0..degree], 0..) |u, idx_i| { for (neighbors_buf[idx_i + 1 .. degree]) |w| { if (adj[u * n + w] == 0) fill += 1; } } const better = fill < best_fill or (fill == best_fill and (degree < best_degree or (degree == best_degree and (best_var == null or v < best_var.?)))); if (better) { best_var = v; best_fill = fill; best_degree = degree; } } const chosen = best_var orelse break; var degree: usize = 0; for (0..n) |u| { if (u == chosen or eliminated[u]) continue; if (adj[chosen * n + u] != 0) { neighbors_buf[degree] = u; degree += 1; } } for (neighbors_buf[0..degree], 0..) |u, idx_i| { for (neighbors_buf[idx_i + 1 .. degree]) |w| { adj[u * n + w] = 1; adj[w * n + u] = 1; } } for (neighbors_buf[0..degree]) |u| { adj[chosen * n + u] = 0; adj[u * n + chosen] = 0; } eliminated[chosen] = true; order[out_idx] = def_names[chosen]; } return order;}fn collectDefinedRefs( expr: *PExpr, name_to_idx: *const std.StringHashMap(usize), deps: *std.ArrayList(usize), allocator: Allocator,) void { switch (expr.head) { .defined => |d| { if (name_to_idx.get(d.name)) |idx| { for (deps.items) |existing| { if (existing == idx) return; } deps.append(allocator, idx) catch {}; } }, else => {}, } for (expr.args) |arg| { collectDefinedRefs(arg, name_to_idx, deps, allocator); }}test "definition order topological respects dependencies" { const allocator = std.testing.allocator; var defs = Definitions.init(allocator); defer defs.deinit(); const expr_b = try PExpr.initWithArgs(allocator, .{ .flip = {} }, &[_]*PExpr{ try PExpr.initWithArgs(allocator, .{ .const_native = .{ .float = 0.5 } }, &[_]*PExpr{}), }); try defs.define("b", expr_b); const expr_a = try PExpr.initWithArgs(allocator, .{ .defined = .{ .name = "b" } }, &[_]*PExpr{}); try defs.define("a", expr_a); const order = (try buildDefinitionOrder(allocator, &defs, .topological)).?; defer { order.deinit(allocator); allocator.destroy(order); } const idx_b = order.getIndex("b").?; const idx_a = order.getIndex("a").?; try std.testing.expect(idx_b < idx_a);}test "definition order min-fill chooses low-fill variable first" { const allocator = std.testing.allocator; var defs = Definitions.init(allocator); defer defs.deinit(); const expr_d = try PExpr.initWithArgs(allocator, .{ .flip = {} }, &[_]*PExpr{ try PExpr.initWithArgs(allocator, .{ .const_native = .{ .float = 0.4 } }, &[_]*PExpr{}), }); try defs.define("d", expr_d); const expr_b = try PExpr.initWithArgs(allocator, .{ .defined = .{ .name = "d" } }, &[_]*PExpr{}); try defs.define("b", expr_b); const expr_c = try PExpr.initWithArgs(allocator, .{ .defined = .{ .name = "d" } }, &[_]*PExpr{}); try defs.define("c", expr_c); const expr_a = try PExpr.initWithArgs(allocator, .{ .app = {} }, &[_]*PExpr{ try PExpr.initWithArgs(allocator, .{ .defined = .{ .name = "b" } }, &[_]*PExpr{}), try PExpr.initWithArgs(allocator, .{ .defined = .{ .name = "c" } }, &[_]*PExpr{}), }); try defs.define("a", expr_a); const order = (try buildDefinitionOrder(allocator, &defs, .min_fill)).?; defer { order.deinit(allocator); allocator.destroy(order); } const idx_a = order.getIndex("a").?; try std.testing.expectEqual(@as(i32, 0), idx_a);}Source: lib/pluck/src/root.zig:10
zig
pub const definition_order = @import("order.zig");Complete caller list for definition_order.buildDefinitionOrder
7 direct callers.
lib.pluck.src.order.test_definition_order_min-fill_chooses_low-fill_variable_first[function] — test source atlib/pluck/src/order.zig:311in nearest public ownertiny.pluck.definition_orderlib.pluck.src.order.test_definition_order_topological_respects_dependencies[function] — test source atlib/pluck/src/order.zig:287in nearest public ownertiny.pluck.definition_orderlib.pluck.src.toplevel.query.initExactSamplesQueryState[method] — private source atlib/pluck/src/toplevel/query.zig:918in nearest public ownertiny.pluck.toplevel.querylib.pluck.src.toplevel.query.initLpsmcQueryState[method] — private source atlib/pluck/src/toplevel/query.zig:1390in nearest public ownertiny.pluck.toplevel.querytiny.pluck.toplevel.query.processMarginalQueryInternal[method] atlib/pluck/src/toplevel/query.zig:739tiny.pluck.toplevel.query.processPosteriorQueryInternal[method] atlib/pluck/src/toplevel/query.zig:819tiny.pluck.toplevel.query.runQuery[method] atlib/pluck/src/toplevel/query.zig:220
Audit
| Definitions | 7 |
|---|---|
| Public names | 7 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |