app: stacked_cores: define an "or gate" sim + script for post-processing

extract transfer characteristics with e.g.
```
extract_meas.py ../../../out/applications/stacked_cores/52-or--0.0004rad-5000ctl_cond-20000coupling_cond-2000ps-100ps-3ctl-3coupling-3_1_winding-49999998976e0-drive- 2e-9 4e-9 8e-9
```
This commit is contained in:
colin 2022-11-01 00:11:50 -07:00
parent 9d2fbf8b07
commit 87366cf473
2 changed files with 229 additions and 27 deletions

View File

@ -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:]]))

View File

@ -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(&params, 2 /* sender core */, s0_loops, 1 + 2*slots_per_asym /* slot offset */, net_slots);
let name = asymmetric_binary_gate_name(
&params, "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,
);
}
}
}
}
}