lib/machine/src/explore/input.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const explore = @import("root.zig");
  2 const profile = @import("../profile/root.zig");
  3 const std = @import("std");
  4 
  5 const alternative_limit: u8 = 5;
  6 
  7 pub fn Generator(comptime capacity_value: explore.InputCapacity) type {
  8     return struct {
  9         seed: explore.Seed,
 10         plan: explore.TemporalPlan,
 11         sequence: u16 = 0,
 12         virtual_time_tick: u64,
 13         phase: explore.InputPhase = .quiet,
 14         burst_remaining: u8 = 0,
 15 
 16         const Self = @This();
 17 
 18         pub const capacity: explore.InputCapacity = capacity_value;
 19         pub const Error: type = explore.GeneratorError;
 20         pub const InitResult: type = explore.Generation(Self);
 21         pub const Site: type = explore.ChoiceSite(
 22             explore.InputAlternative,
 23             capacity.alternatives,
 24         );
 25         pub const NextResult: type = explore.Generation(Site);
 26 
 27         pub fn init(
 28             selected: profile.Profile,
 29             seed: explore.Seed,
 30             plan: explore.TemporalPlan,
 31         ) Error!InitResult {
 32             try profile.validate(selected);
 33             if (plan.end_tick < plan.start_tick) return error.InvalidPlan;
 34             if (plan.steps == 0 or plan.start_tick == plan.end_tick) {
 35                 return .exhausted;
 36             }
 37             if (plan.steps > capacity.steps) {
 38                 return .{ .incomplete = .step_capacity };
 39             }
 40             if (capacity.alternatives < alternative_limit) {
 41                 return .{ .incomplete = .alternative_capacity };
 42             }
 43             return .{ .item = .{
 44                 .seed = seed,
 45                 .plan = plan,
 46                 .virtual_time_tick = plan.start_tick,
 47             } };
 48         }
 49 
 50         pub fn next(self: *const Self) NextResult {
 51             self.assertValid();
 52             if (self.sequence == self.plan.steps or
 53                 self.virtual_time_tick == self.plan.end_tick)
 54             {
 55                 return .exhausted;
 56             }
 57             var site = Site{
 58                 .id = self.siteId(),
 59                 .alternatives = undefined,
 60                 .count = 0,
 61                 .suggested = 0,
 62             };
 63             switch (self.phase) {
 64                 .quiet => self.quietAlternatives(&site),
 65                 .burst => self.burstAlternatives(&site),
 66             }
 67             std.debug.assert(site.count > 0);
 68             site.suggested = self.seed.suggest(.input, self.sequence, site.count);
 69             return .{ .item = site };
 70         }
 71 
 72         pub fn choose(
 73             self: *Self,
 74             site: Site,
 75             index: u8,
 76         ) Error!explore.GeneratedInput {
 77             const current = switch (self.next()) {
 78                 .item => |value| value,
 79                 .exhausted, .incomplete => return error.StaleChoiceSite,
 80             };
 81             if (!sameSite(current, site)) return error.StaleChoiceSite;
 82             if (index >= site.count) return error.ChoiceOutOfRange;
 83             const selected = site.alternatives[index];
 84             self.advance(selected);
 85             self.assertValid();
 86             return selected;
 87         }
 88 
 89         pub fn workload(self: *const Self) explore.InputPhase {
 90             self.assertValid();
 91             return self.phase;
 92         }
 93 
 94         fn quietAlternatives(self: *const Self, site: *Site) void {
 95             self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() });
 96             self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 1) });
 97             self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 2) });
 98             self.addAt(site, 1, .{ .service_result = .failed });
 99             self.addAt(site, 1, .{ .effect_result = .uncertain });
100         }
101 
102         fn burstAlternatives(self: *const Self, site: *Site) void {
103             self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 3) });
104             self.addAt(site, 1, .{ .packet = self.seed.word(.input, self.sequence, 4) });
105             self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 5) });
106             self.addAt(site, 1, .{ .service_result = .succeeded });
107             self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() });
108         }
109 
110         fn addAt(self: *const Self, site: *Site, delta: u64, value: explore.InputValue) void {
111             const tick = self.futureTick(delta) orelse return;
112             std.debug.assert(site.count < site.alternatives.len);
113             site.alternatives[site.count] = .{
114                 .origin = explore.origin(inputSource(value)),
115                 .virtual_time_tick = tick,
116                 .value = value,
117             };
118             site.count += 1;
119         }
120 
121         fn advance(self: *Self, selected: explore.GeneratedInput) void {
122             std.debug.assert(selected.virtual_time_tick > self.virtual_time_tick);
123             std.debug.assert(selected.virtual_time_tick <= self.plan.end_tick);
124             self.virtual_time_tick = selected.virtual_time_tick;
125             self.sequence += 1;
126             switch (selected.value) {
127                 .wait => {
128                     self.phase = .quiet;
129                     self.burst_remaining = 0;
130                 },
131                 .terminal, .entropy, .packet, .service_result, .effect_result => {
132                     self.advanceBurst();
133                 },
134             }
135         }
136 
137         fn advanceBurst(self: *Self) void {
138             switch (self.phase) {
139                 .quiet => {
140                     const width = self.seed.word(.input, self.sequence, 6) % 3;
141                     self.phase = .burst;
142                     self.burst_remaining = 2 + @as(u8, @intCast(width));
143                 },
144                 .burst => {
145                     std.debug.assert(self.burst_remaining > 0);
146                     self.burst_remaining -= 1;
147                     if (self.burst_remaining == 0) self.phase = .quiet;
148                 },
149             }
150         }
151 
152         fn futureTick(self: *const Self, delta: u64) ?u64 {
153             std.debug.assert(self.virtual_time_tick <= self.plan.end_tick);
154             const remaining = self.plan.end_tick - self.virtual_time_tick;
155             if (delta == 0 or delta > remaining) return null;
156             return self.virtual_time_tick + delta;
157         }
158 
159         fn longDelay(self: *const Self) u64 {
160             return 4 + self.seed.word(.input, self.sequence, 7) % 13;
161         }
162 
163         fn siteId(self: *const Self) explore.SiteId {
164             return .{
165                 .stream = .input,
166                 .sequence = self.sequence,
167                 .virtual_time_tick = self.virtual_time_tick,
168             };
169         }
170 
171         fn assertValid(self: *const Self) void {
172             std.debug.assert(self.sequence <= self.plan.steps);
173             std.debug.assert(self.sequence <= capacity.steps);
174             std.debug.assert(self.virtual_time_tick >= self.plan.start_tick);
175             std.debug.assert(self.virtual_time_tick <= self.plan.end_tick);
176             if (self.phase == .quiet) std.debug.assert(self.burst_remaining == 0);
177         }
178 
179         fn sameSite(expected: Site, actual: Site) bool {
180             if (!std.meta.eql(expected.id, actual.id)) return false;
181             if (expected.count != actual.count) return false;
182             if (expected.suggested != actual.suggested) return false;
183             for (expected.values(), actual.values()) |left, right| {
184                 if (!std.meta.eql(left, right)) return false;
185             }
186             return true;
187         }
188     };
189 }
190 
191 fn inputSource(value: explore.InputValue) profile.DeterminismSource {
192     return switch (value) {
193         .wait => .virtual_time,
194         .terminal => .terminal_input,
195         .entropy => .entropy_input,
196         .packet => .packet_payload,
197         .service_result => .host_service_result,
198         .effect_result => .effect_result,
199     };
200 }