henad_core/explore/spec.rs
1//! Sweep specs, as a spec file or the command line writes them.
2
3use std::time::Duration;
4
5use crate::explore::design::DesignKind;
6use crate::explore::factor::FactorSpec;
7use crate::explore::reducer::ReducerSpec;
8use crate::explore::search::SearchSpec;
9use crate::explore::seed::SeedScheme;
10use crate::explore::stop::StopSpec;
11
12/// Prefix of the column that holds an action's tick, as in `action.second_wave`.
13pub const ACTION_COLUMN_PREFIX: &str = "action.";
14
15/// A sweep as written, with parameter values still in text.
16///
17/// [`SweepSpec::plan`] checks it against a model and lists its configs.
18#[derive(Debug, Clone, PartialEq)]
19pub struct SweepSpec {
20 /// Id of the model to run.
21 pub model: String,
22 /// Values every config shares, as `(id, value)` pairs in the form that `--set` accepts.
23 pub fixed: Vec<(String, String)>,
24 /// Length and end of each run, and the number of runs per config.
25 pub run: RunSettings,
26 /// Ticks each run samples, and the values it keeps from them.
27 pub measure: MeasureSettings,
28 /// Seeds of the runs.
29 pub seeds: SeedSettings,
30 /// Actions every run fires. Actions due at the same tick fire in list order.
31 pub actions: Vec<ActionSpec>,
32 /// Blocks whose configs the sweep runs, in order. An empty list runs the fixed values alone.
33 pub blocks: Vec<BlockSpec>,
34 /// Search that picks the configs to run instead of blocks, `None` for a sweep.
35 ///
36 /// Each candidate of a search runs [`RunSettings::replicates`] times, over the fixed values and actions above.
37 pub search: Option<SearchSpec>,
38}
39
40impl SweepSpec {
41 /// Returns a spec for `model` with default settings and no blocks.
42 pub fn new(model: impl Into<String>) -> Self {
43 Self {
44 model: model.into(),
45 fixed: Vec::new(),
46 run: RunSettings::default(),
47 measure: MeasureSettings::default(),
48 seeds: SeedSettings::default(),
49 actions: Vec::new(),
50 blocks: Vec::new(),
51 search: None,
52 }
53 }
54}
55
56/// A model action a sweep fires in every run, as written.
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct ActionSpec {
59 /// Id of the action the model declares.
60 pub id: String,
61 /// Name that factors and output columns use for the action, unique within a spec.
62 pub name: String,
63 /// Tick the action fires at, where no block varies it.
64 ///
65 /// Tick 0 fires before the first step, and any later tick after the step that reaches it.
66 pub tick: u64,
67}
68
69impl ActionSpec {
70 /// Returns action `id` at `tick`, with `id` as its name.
71 pub fn new(id: impl Into<String>, tick: u64) -> Self {
72 let id = id.into();
73 Self {
74 name: id.clone(),
75 id,
76 tick,
77 }
78 }
79
80 /// Returns the name of the column that holds the action's tick, as in `action.second_wave`.
81 pub fn column_name(&self) -> String {
82 format!("{ACTION_COLUMN_PREFIX}{}", self.name)
83 }
84}
85
86/// Factors combined under one design, as written.
87#[derive(Debug, Clone, Default, PartialEq)]
88pub struct BlockSpec {
89 /// Design that combines the factors into configs.
90 pub design: DesignKind,
91 /// Factors of the block. A table design gets its factors from its table, and this list stays empty.
92 pub factors: Vec<FactorSpec>,
93 /// Seed of a sampled design's draws, or `None` to derive it with [`design_seed`] from the root seed and the block.
94 ///
95 /// [`design_seed`]: crate::explore::seed::design_seed
96 pub design_seed: Option<u64>,
97}
98
99/// Length and end of each run, and the number of runs per config.
100#[derive(Debug, Clone, PartialEq)]
101pub struct RunSettings {
102 /// Number of ticks stepped after the warm-up.
103 pub steps: u64,
104 /// Number of ticks stepped before the first sample.
105 pub warmup: u64,
106 /// Number of runs per config, each with its own seed.
107 pub replicates: u64,
108 /// Condition that ends a run at the first sample where it holds.
109 pub stop: Option<StopSpec>,
110 /// Wall-clock time after which a run is abandoned, checked between slices of steps. On a GPU track, a run's
111 /// clock counts its share of the time the sweep spends on the tracks.
112 ///
113 /// Note that a timed-out run depends on the machine, so no plan or results hash covers the timeout.
114 pub timeout: Option<Duration>,
115}
116
117impl Default for RunSettings {
118 fn default() -> Self {
119 Self {
120 steps: 1000,
121 warmup: 0,
122 replicates: 1,
123 stop: None,
124 timeout: None,
125 }
126 }
127}
128
129/// Ticks a run samples, and the values it keeps from them.
130#[derive(Debug, Clone, PartialEq)]
131pub struct MeasureSettings {
132 /// Ticks between two samples, counted from the end of the warm-up.
133 pub stats_every: u64,
134 /// Ticks between two rows of the series, a multiple of `stats_every`.
135 ///
136 /// The final sample is always a row. A value of 0 keeps no series at all.
137 pub series_every: u64,
138 /// Whether every column other than a histogram bucket gets the [`ReducerKind::DEFAULTS`] reducers.
139 ///
140 /// [`ReducerKind::DEFAULTS`]: crate::explore::reducer::ReducerKind::DEFAULTS
141 pub default_reducers: bool,
142 /// Reducers added after the defaults.
143 pub reducers: Vec<ReducerSpec>,
144}
145
146impl Default for MeasureSettings {
147 fn default() -> Self {
148 Self {
149 stats_every: 1,
150 series_every: 1,
151 default_reducers: true,
152 reducers: Vec::new(),
153 }
154 }
155}
156
157/// Root seed, and the scheme that derives each run's seed from it.
158#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
159pub struct SeedSettings {
160 /// Seed each run's seed is derived from.
161 pub root: u64,
162 /// Scheme that derives each run's seed from [`Self::root`].
163 pub scheme: SeedScheme,
164}