diff --git a/crates/applications/stacked_cores/scripts/extract_meas.py b/crates/applications/stacked_cores/scripts/extract_meas.py new file mode 100755 index 0000000..6c70f46 --- /dev/null +++ b/crates/applications/stacked_cores/scripts/extract_meas.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +""" +invoke with the path to a meas.csv file for the stacked_core 51-xx or later demos +to extract higher-level info from them. +""" + +import os +import sys +import re + +from stacked_cores import load_csv, labeled_rows, last_row_before_t, extract_m +from stacked_cores_39xx import extract_polarity + +class MeasRow: + def __init__(self, t_sec: float, m: list): + self.t_sec = t_sec + self.m = m + + def __repr__(self) -> str: + m = ", ".join(f"{v:6}" for v in self.m) + return f"MeasRow({self.t_sec}, [{m}])" + + @staticmethod + def from_dict(row_data: dict) -> 'MeasRow': + t_sec = row_data["time"] + m = [int(m + 0.5) for m in extract_m(row_data)] + return MeasRow(t_sec, m) + +def format_float_tuple(t: tuple) -> str: + formatted_elems = [f"{e:= 05.3f}," for e in t] + return f"({' '.join(formatted_elems)})" + +def format_list(l: list) -> str: + if len(l) == 0: return "[]" + if len(l) == 1: return f"{l}" + formatted_elems = [f"\t{e}" for e in l] + return "\n".join(["["] + formatted_elems + ["]"]) + +def indented(s: str) -> str: + return s.replace('\n', '\n\t') + +class ParameterizedMeas: + def __init__(self, meas = None): + self.meas = meas or {} + + def add_meas(self, params: tuple, meas_rows: list): + self.meas[tuple(params)] = meas_rows + + def __repr__(self) -> str: + meas_entries = "\n".join( + f"\t{format_float_tuple(k)}: {indented(format_list(v))}," for (k, v) in sorted(self.meas.items()) + ) + return f"ParameterizedMeas({{\n{meas_entries}\n}})" + +def extract_rows(path: str, times: list) -> list: + header, raw_rows = load_csv(path) + rows = labeled_rows(header, raw_rows) + + meas_rows = [] + for t in times: + row = last_row_before_t(rows, t) + if not row: return None + meas_rows.append(MeasRow.from_dict(row)) + + # validate the sim has run to completion + if meas_rows[-1].t_sec < 0.95 * t: return None + meas_rows[-1].t_sec = t # make pretty + + return meas_rows + +def parse_param(s: str) -> float: + """ parse a parameter in the form of 'p050' or 'n0015' or '000' """ + if s == "000": + return 0.0 + + sign = {'n': -1, 'p': 1}[s[0]] + mag = int(s[1:]) + max_mag = 10**(len(s[1:]) - 1) + return sign * mag / max_mag + +def extract_params(pstr: str) -> list: + """ extract parameters from a string like -n100-000 """ + pieces = [p for p in pstr.split("-") if p] + return [parse_param(p) for p in pieces] + +def extract_parameterized_meas(stem: str, times: list) -> ParameterizedMeas: + """ given some stem, parse all parameterized measurements associated with that stem """ + base_dir, prefix = os.path.split(stem) + + built = ParameterizedMeas() + for entry in os.listdir(base_dir): + if entry.startswith(prefix): + meas_rows = extract_rows(os.path.join(base_dir, entry, "meas.csv"), times) + if not meas_rows: continue + params = extract_params(entry[len(prefix):]) + built.add_meas(params, meas_rows) + + return built + + +if __name__ == "__main__": + print(extract_parameterized_meas(sys.argv[1], [float(f) for f in sys.argv[2:]])) diff --git a/crates/applications/stacked_cores/src/main.rs b/crates/applications/stacked_cores/src/main.rs index 82326d5..de69b99 100644 --- a/crates/applications/stacked_cores/src/main.rs +++ b/crates/applications/stacked_cores/src/main.rs @@ -1324,6 +1324,25 @@ fn drive_map_fork_then_join_m2_out(amp0: f32) -> [[ClockState; 4]; 3] { ] } +#[allow(unused)] +fn drive_map_or_gate(amp0: f32, amp1: f32) -> [[ClockState; 4]; 4] { + use ClockState as C; + // amplitudes are inverted from what you would expect. + // hold(-1) puts the core into a positive M + [ + // init S0 pos, S1 pos; charge S2 neg, S3 neg + [C::release(-amp0), C::release(-amp1), C::release_high(), C::release_high()], + // clear S0 -> S2, S1 -> S2 + // NB: we might slightly prefer to do this in two separate steps, but doing it in one cycle is + // quicker to simulate. + [C::hold_high(), C::hold_high(), C::float(), C::float()], + // relax S0, S1, so that S2 isn't unnecessarily loaded in the next cycle + [C::release_high(), C::release_high(), C::float(), C::float()], + // clear S2 -> S3 + [C::hold_high(), C::hold_high(), C::hold_high(), C::float()], + ] +} + fn asymmetric_inverter_name(p: &Params, sim_id: &str, windings: u32, init_flt: f32) -> String { let init_int = (init_flt.abs() * 100.0 + 0.5) as u32; let init_level = if init_flt > 0.0 { @@ -1337,13 +1356,8 @@ fn asymmetric_inverter_name(p: &Params, sim_id: &str, windings: u32, init_flt: f let s_major = p.s_major; let coupling_loops = p.coupling_loops; - let mut input_leading = p.input_magnitude as u64; - let mut input_exp = 0; - while input_leading != 0 && input_leading % 10 == 0 { - input_leading /= 10; - input_exp += 1; - } - format!("{sim_id}-{s_major}rad-{coupling_loops}coupling-{windings}_1_winding-{input_leading}e{input_exp}-drive-{init_level}") + let input_str = exp_format(p.input_magnitude); + format!("{sim_id}-{s_major}rad-{coupling_loops}coupling-{windings}_1_winding-{input_str}-drive-{init_level}") } @@ -1360,18 +1374,11 @@ fn asymmetric_inverter_name_v2(p: &Params, sim_id: &str, ctl_loops: u32, couplin let s_major = p.s_major; - let mut input_leading = p.input_magnitude as u64; - let mut input_exp = 0; - while input_leading != 0 && input_leading % 10 == 0 { - input_leading /= 10; - input_exp += 1; - } - format!("{sim_id}-{s_major}rad-{ctl_loops}ctl-{coupling_loops}coupling-{windings}_1_winding-{input_leading}e{input_exp}-drive-{init_level}") + let input_str = exp_format(p.input_magnitude); + format!("{sim_id}-{s_major}rad-{ctl_loops}ctl-{coupling_loops}coupling-{windings}_1_winding-{input_str}-drive-{init_level}") } -/// v3 has coupling_loops specified externally, -/// and encodes conductivities + clocks -fn asymmetric_inverter_name_v3(p: &Params, sim_id: &str, ctl_loops: u32, coupling_loops: u32, windings: u32, init_flt: f32) -> String { +fn init_float_str(init_flt: f32) -> String { let init_int = (init_flt.abs() * 1000.0 + 0.5) as u32; let init_int_str = if init_int % 10 == 0 { format!("{:03}", init_int / 10) @@ -1379,13 +1386,29 @@ fn asymmetric_inverter_name_v3(p: &Params, sim_id: &str, ctl_loops: u32, couplin format!("{:04}", init_int) }; - let init_level = if init_flt > 0.0 { + if init_flt > 0.0 { format!("p{init_int_str}") } else if init_flt < 0.0 { format!("n{init_int_str}") } else { init_int_str - }; + } +} + +fn exp_format(e: f32) -> String { + let mut e_leading = e as u64; + let mut e_exp = 0; + while e_leading != 0 && e_leading % 10 == 0 { + e_leading /= 10; + e_exp += 1; + } + format!("{}e{}", e_leading, e_exp) +} + +/// v3 has coupling_loops specified externally, +/// and encodes conductivities + clocks +fn asymmetric_inverter_name_v3(p: &Params, sim_id: &str, ctl_loops: u32, coupling_loops: u32, windings: u32, init_flt: f32) -> String { + let init_level = init_float_str(init_flt); let s_major = p.s_major; let ctl_cond = p.ctl_conductivity as u64; @@ -1393,13 +1416,22 @@ fn asymmetric_inverter_name_v3(p: &Params, sim_id: &str, ctl_loops: u32, couplin let clock_length = (p.clock_phase_duration * 1e12 + 0.5) as u64; let clock_decay = (p.clock_decay * 1e12 + 0.5) as u64; - let mut input_leading = p.input_magnitude as u64; - let mut input_exp = 0; - while input_leading != 0 && input_leading % 10 == 0 { - input_leading /= 10; - input_exp += 1; - } - format!("{sim_id}-{s_major}rad-{ctl_cond}ctl_cond-{coupling_cond}coupling_cond-{clock_length}ps-{clock_decay}ps-{ctl_loops}ctl-{coupling_loops}coupling-{windings}_1_winding-{input_leading}e{input_exp}-drive-{init_level}") + let input_str = exp_format(p.input_magnitude); + format!("{sim_id}-{s_major}rad-{ctl_cond}ctl_cond-{coupling_cond}coupling_cond-{clock_length}ps-{clock_decay}ps-{ctl_loops}ctl-{coupling_loops}coupling-{windings}_1_winding-{input_str}-drive-{init_level}") +} + +fn asymmetric_binary_gate_name(p: &Params, sim_id: &str, ctl_loops: u32, coupling_loops: u32, windings: u32, init_flt_a: f32, init_flt_b: f32) -> String { + let init_level_a = init_float_str(init_flt_a); + let init_level_b = init_float_str(init_flt_b); + + let s_major = p.s_major; + let ctl_cond = p.ctl_conductivity as u64; + let coupling_cond = p.coupling_conductivity as u64; + let clock_length = (p.clock_phase_duration * 1e12 + 0.5) as u64; + let clock_decay = (p.clock_decay * 1e12 + 0.5) as u64; + + let input_str = exp_format(p.input_magnitude); + format!("{sim_id}-{s_major}rad-{ctl_cond}ctl_cond-{coupling_cond}coupling_cond-{clock_length}ps-{clock_decay}ps-{ctl_loops}ctl-{coupling_loops}coupling-{windings}_1_winding-{input_str}-drive-{init_level_a}-{init_level_b}") } /// couple `sender` to the `sender+1` core by using `loops` loops, including a crossover so that @@ -5135,7 +5167,7 @@ fn main() { } } - if true { + if false { for init_set in [ &[ // targeted for (5e2, 5e3, ps(1000), ps(50), 9, 1, um(400), 1e10) @@ -5647,6 +5679,74 @@ fn main() { } } } + + if true { + for init_set in [ + &[ + // targeted + ][..], + &[ + // establish the domain/range + ( 1.00, 1.00), + (-1.00, -1.00), + (-1.00, 1.00), + ( 1.00, -1.00), + ][..], + &[ + (0.00, 0.00), + (-1.00, 0.00), + ][..], + ] { + for (ctl_cond, coupling_cond, clock_duration, clock_decay, coupling_loops, s0_loops, s_major, cur_flt) in [ + (5e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 5e10), + (5e2, 2e4, ps(2000), ps(100), 3, 1, um(400), 5e10), + + (1e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 2e10), + (2e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 1e10), + (2e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 2e10), + (2e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 3e10), + (5e3, 2e4, ps(2000), ps(100), 3, 1, um(400), 3e10), + + ] { + for &(init_flt_a, init_flt_b) in init_set { + // coupling loops (M0 -> M2) + (M1 -> M2) + (M2 -> M3) + control slots + let slots_per_asym = 2*s0_loops; + let net_slots = 3*slots_per_asym + 1; + let mut params = params_v2 + .with_clock_phase_duration(clock_duration) + .with_clock_decay(clock_decay) + .with_ctl_conductivity(ctl_cond) + .with_coupling_conductivity(coupling_cond) + .with_s_major(s_major) + .with_coupling_loops(coupling_loops) + .with_input_magnitude(cur_flt) + // control loops + .with_coupling(0, 0, 0, net_slots, CouplingMethod::Control) + .with_coupling(1, 1, 0, net_slots, CouplingMethod::Control) + .with_coupling(2, 2, 0, net_slots, CouplingMethod::Control) + .with_coupling(3, 3, 0, net_slots, CouplingMethod::Control) + ; + for i in 0..slots_per_asym { + // couple input core 0 to core 2 + params = params.with_coupling(0, 2, 1+i, net_slots, CouplingMethod::Outside); + // couple input core 1 to core 2 + params = params.with_coupling(1, 2, 1+i + slots_per_asym, net_slots, CouplingMethod::Direct); + } + // couple OR gate core 2 to output core 3 + params = couple_asymmetric_buffer(¶ms, 2 /* sender core */, s0_loops, 1 + 2*slots_per_asym /* slot offset */, net_slots); + + let name = asymmetric_binary_gate_name( + ¶ms, "52-or-", coupling_loops /* ctl loops */, coupling_loops, 2*s0_loops + 1, init_flt_a, init_flt_b + ); + run_sim( + &name, + drive_map_or_gate(init_flt_a, init_flt_b), + params, + ); + } + } + } + } }