tiny.pluck.wmc
Defined in tiny.pluck.
API (7)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/pluck/src/root.zig:28
zig
pub const wmc = @import("wmc.zig");Source: lib/pluck/src/wmc.zig
zig
const std = @import("std");const sys = @import("sys");const Allocator = std.mem.Allocator;const bdd = @import("bdd.zig");const Bdd = bdd.Bdd;const Manager = bdd.Manager;const WmcParams = bdd.WmcParams;const weight_dd = @import("weight.zig");const WeightDD = weight_dd.WeightDD;const Weight = weight_dd.Weight;const runtime = @import("runtime.zig");const RuntimeValue = runtime.RuntimeValue;pub const World = runtime.GuardedWorld;pub const WeightedResult = struct { value: *RuntimeValue, probability: f64,};pub const DeferredCaches = struct { deferred: std.AutoHashMap(u64, f64), weighted: std.AutoHashMap(u64, f64), pub fn init(allocator: Allocator) DeferredCaches { return .{ .deferred = std.AutoHashMap(u64, f64).init(allocator), .weighted = std.AutoHashMap(u64, f64).init(allocator), }; } pub fn deinit(self: *DeferredCaches) void { self.weighted.deinit(); self.deferred.deinit(); }};const ParallelWmcContext = struct { cache_allocator: Allocator, worlds: []const World, weighted_results: []WeightedResult, wmc_params: *const WmcParams, weight_dd: *const WeightDD, weight_root: Weight,};fn parallelWmcWorker(ctx: *const ParallelWmcContext, start: usize, end: usize) void { var cache = std.AutoHashMap(u64, f64).init(ctx.cache_allocator); defer cache.deinit(); for (start..end) |i| { ctx.weighted_results[i] = WeightedResult{ .value = ctx.worlds[i].value, .probability = weight_dd.wmcWeightedWithCache( ctx.weight_dd, ctx.worlds[i].guard, ctx.weight_root, ctx.wmc_params, &cache, ), }; }}pub fn computeWmcParallel( result_allocator: Allocator, cache_allocator: Allocator, worlds: []const World, wmc_params: *const WmcParams, weight_dd_ctx: *const WeightDD, weight_root: Weight, num_threads: ?usize,) ![]WeightedResult { if (!sys.thread.threadsSupported()) { return computeWmcSequential(result_allocator, worlds, wmc_params, weight_dd_ctx, weight_root); } else { if (worlds.len == 0) { return try result_allocator.alloc(WeightedResult, 0); } const weighted_results = try result_allocator.alloc(WeightedResult, worlds.len); errdefer result_allocator.free(weighted_results); const worker_count = @max(1, num_threads orelse 1); const ctx = ParallelWmcContext{ .cache_allocator = cache_allocator, .worlds = worlds, .weighted_results = weighted_results, .wmc_params = wmc_params, .weight_dd = weight_dd_ctx, .weight_root = weight_root, }; if (worker_count == 1 or worlds.len < 2) { parallelWmcWorker(&ctx, 0, worlds.len); return weighted_results; } const total_threads = @min(worker_count, worlds.len); const worker_threads = total_threads - 1; const threads = try result_allocator.alloc(sys.thread.JoinHandle, worker_threads); defer result_allocator.free(threads); var spawned_threads: usize = 0; errdefer { for (threads[0..spawned_threads]) |thread| { thread.join(); } } var start: usize = 0; const base_chunk_size = worlds.len / total_threads; const remainder = worlds.len % total_threads; for (threads, 0..) |*thread, i| { const extra: usize = if (i < remainder) 1 else 0; const chunk_size = base_chunk_size + extra; const end = start + chunk_size; thread.* = try sys.thread.spawn(parallelWmcWorker, .{ &ctx, start, end }); spawned_threads += 1; start = end; } if (start < worlds.len) { parallelWmcWorker(&ctx, start, worlds.len); } for (threads) |thread| { thread.join(); } return weighted_results; }}pub fn computeWmcSequential( allocator: Allocator, worlds: []const World, wmc_params: *const WmcParams, weight_dd_ctx: *const WeightDD, weight_root: Weight,) ![]WeightedResult { var weighted_results = try allocator.alloc(WeightedResult, worlds.len); errdefer allocator.free(weighted_results); var cache = std.AutoHashMap(u64, f64).init(allocator); defer cache.deinit(); for (worlds, 0..) |world, i| { weighted_results[i] = WeightedResult{ .value = world.value, .probability = weight_dd.wmcWeightedWithCache(weight_dd_ctx, world.guard, weight_root, wmc_params, &cache), }; } return weighted_results;}test "DeferredCaches separates deferred and weighted key spaces" { var caches = DeferredCaches.init(std.testing.allocator); defer caches.deinit(); const key: u64 = 42; try caches.deferred.put(key, 1.25); try caches.weighted.put(key, 2.5); try std.testing.expectApproxEqAbs(@as(f64, 1.25), caches.deferred.get(key).?, 1e-12); try std.testing.expectApproxEqAbs(@as(f64, 2.5), caches.weighted.get(key).?, 1e-12);}test "parallel WMC produces same results as sequential" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var manager = try Manager.init(allocator); defer manager.deinit(); var weight_ctx = try WeightDD.init(allocator, &manager); defer weight_ctx.deinit(); const weight_root = try weight_ctx.leaf(1.0); const num_worlds = 32; const worlds = try allocator.alloc(World, num_worlds); var vars: [5]Bdd = undefined; for (0..5) |i| { vars[i] = try manager.newVar(true); } for (0..num_worlds) |i| { const val = try RuntimeValue.initNative(allocator, .{ .int = @intCast(i) }); var guard = Bdd.TRUE; for (0..5) |j| { const use_var = (i >> @intCast(j)) & 1 == 1; if (use_var) { guard = try manager.bddAnd(guard, vars[j]); } } worlds[i] = World{ .value = val, .guard = guard }; } var wmc_params = WmcParams.init(allocator); defer wmc_params.deinit(); for (0..manager.numVars()) |v| { try wmc_params.setWeight(@intCast(v), 0.5, 0.5); } const seq_results = try computeWmcSequential(allocator, worlds, &wmc_params, &weight_ctx, weight_root); defer allocator.free(seq_results); const par_results = try computeWmcParallel(allocator, std.testing.allocator, worlds, &wmc_params, &weight_ctx, weight_root, 2); defer allocator.free(par_results); try std.testing.expectEqual(seq_results.len, par_results.len); for (seq_results, par_results) |seq, par| { try std.testing.expectEqual(seq.value, par.value); try std.testing.expectApproxEqAbs(seq.probability, par.probability, 1e-15); }}test "parallel WMC handles exact divisibility by thread count" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var manager = try Manager.init(allocator); defer manager.deinit(); var weight_ctx = try WeightDD.init(allocator, &manager); defer weight_ctx.deinit(); const weight_root = try weight_ctx.leaf(1.0); const num_worlds = 9; const worlds = try allocator.alloc(World, num_worlds); var vars: [4]Bdd = undefined; for (0..4) |i| { vars[i] = try manager.newVar(true); } for (0..num_worlds) |i| { const val = try RuntimeValue.initNative(allocator, .{ .int = @intCast(i) }); var guard = Bdd.TRUE; for (0..4) |j| { const use_var = (i >> @intCast(j)) & 1 == 1; if (use_var) { guard = try manager.bddAnd(guard, vars[j]); } } worlds[i] = World{ .value = val, .guard = guard }; } var wmc_params = WmcParams.init(allocator); defer wmc_params.deinit(); for (0..manager.numVars()) |v| { try wmc_params.setWeight(@intCast(v), 0.5, 0.5); } const seq_results = try computeWmcSequential(allocator, worlds, &wmc_params, &weight_ctx, weight_root); defer allocator.free(seq_results); const par_results = try computeWmcParallel(allocator, std.testing.allocator, worlds, &wmc_params, &weight_ctx, weight_root, 2); defer allocator.free(par_results); try std.testing.expectEqual(seq_results.len, par_results.len); for (seq_results, par_results, 0..) |seq, par, i| { try std.testing.expectEqual(seq.value, par.value); try std.testing.expectApproxEqAbs(seq.probability, par.probability, 1e-15); try std.testing.expect(par.probability >= 0 and par.probability <= 1); _ = i; }}Audit
| Definitions | 7 |
|---|---|
| Public names | 10 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |