rosetta-eclab 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rosetta_eclab/__init__.py +0 -0
- rosetta_eclab/cli.py +113 -0
- rosetta_eclab/data/stats.json +1 -0
- rosetta_eclab/data/stats_features.npy +0 -0
- rosetta_eclab/debug.py +85 -0
- rosetta_eclab/engine.py +203 -0
- rosetta_eclab/evaluate.py +196 -0
- rosetta_eclab/fallback.py +130 -0
- rosetta_eclab/golden.py +74 -0
- rosetta_eclab/library.py +708 -0
- rosetta_eclab/misses.py +39 -0
- rosetta_eclab/mprfile.py +178 -0
- rosetta_eclab-0.1.1.dist-info/METADATA +136 -0
- rosetta_eclab-0.1.1.dist-info/RECORD +31 -0
- rosetta_eclab-0.1.1.dist-info/WHEEL +5 -0
- rosetta_eclab-0.1.1.dist-info/entry_points.txt +2 -0
- rosetta_eclab-0.1.1.dist-info/licenses/LICENSE +674 -0
- rosetta_eclab-0.1.1.dist-info/top_level.txt +2 -0
- spike2/__init__.py +0 -0
- spike2/agent.py +372 -0
- spike2/api_check.py +117 -0
- spike2/baselines.py +153 -0
- spike2/corpus.py +162 -0
- spike2/dsl.py +167 -0
- spike2/elim.py +52 -0
- spike2/features.py +137 -0
- spike2/lock.py +59 -0
- spike2/run.py +218 -0
- spike2/score.py +223 -0
- spike2/tasks.py +355 -0
- spike2/vocab.py +126 -0
rosetta_eclab/misses.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""List the namer's misses on a v2 split with the engine's ranking (development aid; never on the hold-out)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
from spike2 import corpus, score, tasks
|
|
9
|
+
|
|
10
|
+
from .evaluate import answer
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> int:
|
|
14
|
+
warnings.filterwarnings("ignore")
|
|
15
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
16
|
+
split = sys.argv[1] if len(sys.argv) > 1 else "test"
|
|
17
|
+
assert split in ("dev", "test", "holdout", "holdout2", "holdout3", "holdout4"), "development (spent) splits only"
|
|
18
|
+
for t in tasks.load(split):
|
|
19
|
+
if t["bucket"] not in ("main", "decoy"):
|
|
20
|
+
continue
|
|
21
|
+
x, dtype, kv, kc = tasks.hidden_and_known(t)
|
|
22
|
+
a = answer(x, dtype, list(kc.values()), t["technique"], tasks.task_vocabulary(t),
|
|
23
|
+
exclude_group=corpus.source_group(t["file"]))
|
|
24
|
+
r = {"status": "ok", **a}
|
|
25
|
+
if t["bucket"] == "main" and score.correct(t, r):
|
|
26
|
+
continue
|
|
27
|
+
if t["bucket"] == "decoy" and not score.hallucinated(t, r):
|
|
28
|
+
continue
|
|
29
|
+
truth = t["truth"]["canonical"] if t["truth"] else f"DECOY {t['decoy']['kind']} {t['decoy'].get('source_name', '')}"
|
|
30
|
+
print(f"\n{t['task_id']} {t['file']} ({t['technique']}) truth={truth} -> {a['verdict']} {a['name']}")
|
|
31
|
+
print(f" known: {', '.join(c.name for c in kc.values())}")
|
|
32
|
+
print(f" evidence: {a.get('evidence', '')[:200]}")
|
|
33
|
+
for row in a.get("ranking", [])[:4]:
|
|
34
|
+
print(f" {row}")
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
raise SystemExit(main())
|
rosetta_eclab/mprfile.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Read a BioLogic EC-Lab .mpr file, including columns the reference parser does not know.
|
|
2
|
+
|
|
3
|
+
Known columns are typed and named with the reference parser's column map. For
|
|
4
|
+
unknown column IDs the byte width is solved from the record size, the storage
|
|
5
|
+
type is chosen by plausibility (finite, sensible magnitudes, integers for
|
|
6
|
+
integer types), and the values are decoded so the namer can identify them.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import itertools
|
|
12
|
+
import logging
|
|
13
|
+
import warnings
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
FLAG_IDS = (1, 2, 3, 21, 31, 65)
|
|
20
|
+
CANDIDATES = {1: ["|u1"], 2: ["<u2", "<i2"], 4: ["<f4", "<u4", "<i4"], 8: ["<f8", "<u8", "<i8"]}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Col:
|
|
25
|
+
position: int
|
|
26
|
+
raw_id: int
|
|
27
|
+
name: str
|
|
28
|
+
unit: str | None
|
|
29
|
+
dtype: str
|
|
30
|
+
known: bool
|
|
31
|
+
values: np.ndarray = field(repr=False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class MprData:
|
|
36
|
+
path: str
|
|
37
|
+
technique: str | None
|
|
38
|
+
n_points: int
|
|
39
|
+
record_size: int
|
|
40
|
+
columns: list[Col]
|
|
41
|
+
notes: list[str]
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def unknown(self) -> list[Col]:
|
|
45
|
+
return [c for c in self.columns if not c.known]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _modules(raw: bytes):
|
|
49
|
+
from yadg import dgutils
|
|
50
|
+
from yadg.extractors.eclab.mpr_columns import module_header_dtypes
|
|
51
|
+
|
|
52
|
+
for module in raw.split(b"MODULE")[1:]:
|
|
53
|
+
for mhd in module_header_dtypes:
|
|
54
|
+
try:
|
|
55
|
+
header = dgutils.read_value(module, 0x0000, mhd)
|
|
56
|
+
if len(module) == mhd.itemsize + header["length"]:
|
|
57
|
+
yield header["short_name"].strip(), header.get("newver", 0) + header["oldver"], module[mhd.itemsize:]
|
|
58
|
+
break
|
|
59
|
+
except UnicodeDecodeError:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _plausibility(v: np.ndarray, dtype: str) -> float:
|
|
64
|
+
x = v.astype("f8")
|
|
65
|
+
fin = np.isfinite(x)
|
|
66
|
+
if fin.mean() < 0.999:
|
|
67
|
+
return 0.0
|
|
68
|
+
y = x[fin]
|
|
69
|
+
if y.size == 0:
|
|
70
|
+
return 0.0
|
|
71
|
+
if dtype.endswith(("f4", "f8")):
|
|
72
|
+
a = np.abs(y[y != 0])
|
|
73
|
+
if a.size and (np.mean((a < 1e-30) | (a > 1e15)) > 0.01):
|
|
74
|
+
return 0.05 # denormal-looking or astronomically large values: wrong type
|
|
75
|
+
return 1.0 if np.mean(np.isclose(y, np.round(y))) < 0.95 else 0.7
|
|
76
|
+
# integers: plausible when they are small relative to the type's range
|
|
77
|
+
span = float(np.max(np.abs(y))) if y.size else 0.0
|
|
78
|
+
width = np.dtype(dtype).itemsize
|
|
79
|
+
return 0.9 if span < 2 ** (8 * width - 4) else 0.2
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def read(path: str | Path, reference: str = "galvani") -> MprData:
|
|
83
|
+
from galvani import BioLogic
|
|
84
|
+
from yadg.extractors.eclab.mpr import parse_columns, process_settings
|
|
85
|
+
|
|
86
|
+
logging.getLogger("yadg").setLevel(logging.CRITICAL)
|
|
87
|
+
warnings.filterwarnings("ignore")
|
|
88
|
+
raw = Path(path).read_bytes()
|
|
89
|
+
if not raw.startswith(b"BIO-LOGIC MODULAR FILE"):
|
|
90
|
+
raise ValueError("not a BioLogic EC-Lab .mpr file")
|
|
91
|
+
technique, data, version = None, None, None
|
|
92
|
+
for name, ver, mdata in _modules(raw):
|
|
93
|
+
if name == "VMP Set":
|
|
94
|
+
try:
|
|
95
|
+
technique, _, _ = process_settings(mdata, "11.50" if ver >= 10 else "10.40")
|
|
96
|
+
except Exception:
|
|
97
|
+
technique = None
|
|
98
|
+
elif name == "VMP data":
|
|
99
|
+
data, version = mdata, ver
|
|
100
|
+
if data is None:
|
|
101
|
+
raise ValueError("no 'VMP data' module")
|
|
102
|
+
n_points = int(np.frombuffer(data, offset=0, dtype="<u4", count=1)[0])
|
|
103
|
+
n_columns = int(np.frombuffer(data, offset=4, dtype="|u1", count=1)[0])
|
|
104
|
+
if version in (10, 11):
|
|
105
|
+
yids = np.frombuffer(data, offset=5, dtype=">u2", count=n_columns).tolist()
|
|
106
|
+
full = np.frombuffer(data, offset=6, dtype="<u2", count=n_columns).tolist()
|
|
107
|
+
offset = 0x3EF
|
|
108
|
+
elif version in (2, 3):
|
|
109
|
+
yids = full = np.frombuffer(data, offset=5, dtype="<u2", count=n_columns).tolist()
|
|
110
|
+
offset = 0x195 if version == 2 else 0x196
|
|
111
|
+
else:
|
|
112
|
+
raise NotImplementedError(f"data module version {version} is not supported yet")
|
|
113
|
+
region = len(data) - offset
|
|
114
|
+
if n_points == 0 or region % n_points:
|
|
115
|
+
raise ValueError(f"record size is not a whole number of bytes ({region} / {n_points})")
|
|
116
|
+
rec = region // n_points
|
|
117
|
+
|
|
118
|
+
names, dtypes, units, _ = parse_columns(yids, technique) # yadg's map: names for known columns
|
|
119
|
+
gmap = {int(k): v for k, v in BioLogic.VMPdata_colID_dtype_map.items()}
|
|
120
|
+
layout = [] # record layout in file order: (raw_id or None for the flags byte, ...)
|
|
121
|
+
for yid, fid in zip(yids, full):
|
|
122
|
+
if yid in FLAG_IDS: # all flag IDs share one byte, at the position of the first one
|
|
123
|
+
if not any(entry[0] is None for entry in layout):
|
|
124
|
+
layout.append((None, "flags", None, "|u1", True))
|
|
125
|
+
continue
|
|
126
|
+
layout.append((fid, None, None, None, None))
|
|
127
|
+
# names/dtypes for data columns, in order, from yadg's map (it skips flags the same way)
|
|
128
|
+
data_names = [(n, d, u) for n, d, u in zip(names, dtypes, units) if n != "flags"]
|
|
129
|
+
filled, j = [], 0
|
|
130
|
+
for entry in layout:
|
|
131
|
+
if entry[0] is None:
|
|
132
|
+
filled.append(entry)
|
|
133
|
+
continue
|
|
134
|
+
n, d, u = data_names[j]
|
|
135
|
+
j += 1
|
|
136
|
+
fid = entry[0]
|
|
137
|
+
known_yadg = not n.startswith("unknown_")
|
|
138
|
+
known_ref = (fid in gmap) if reference == "galvani" else known_yadg
|
|
139
|
+
if known_ref and known_yadg:
|
|
140
|
+
filled.append((fid, n, u, d, True))
|
|
141
|
+
elif known_ref: # galvani knows it, yadg does not
|
|
142
|
+
gname, gd = gmap[fid]
|
|
143
|
+
filled.append((fid, gname.split("/")[0], None, gd, True))
|
|
144
|
+
else:
|
|
145
|
+
filled.append((fid, None, None, None, False))
|
|
146
|
+
known_width = sum(np.dtype(d).itemsize for _, _, _, d, kn in filled if kn)
|
|
147
|
+
unknown_idx = [i for i, e in enumerate(filled) if not e[4]]
|
|
148
|
+
rest = rec - known_width
|
|
149
|
+
notes = []
|
|
150
|
+
best = None
|
|
151
|
+
if unknown_idx:
|
|
152
|
+
combos = [c for c in itertools.product((1, 2, 4, 8), repeat=len(unknown_idx)) if sum(c) == rest]
|
|
153
|
+
for widths in combos[:200]:
|
|
154
|
+
for types in itertools.product(*[CANDIDATES[w] for w in widths]):
|
|
155
|
+
trial = list(filled)
|
|
156
|
+
for i, t in zip(unknown_idx, types):
|
|
157
|
+
trial[i] = (trial[i][0], None, None, t, False)
|
|
158
|
+
arr = np.frombuffer(data, offset=offset, count=n_points,
|
|
159
|
+
dtype=np.dtype([(f"f{i}", e[3]) for i, e in enumerate(trial)]))
|
|
160
|
+
score = sum(_plausibility(arr[f"f{i}"], t) for i, t in zip(unknown_idx, types))
|
|
161
|
+
if best is None or score > best[0]:
|
|
162
|
+
best = (score, trial)
|
|
163
|
+
if best is None:
|
|
164
|
+
raise ValueError(f"cannot fit {len(unknown_idx)} unknown column(s) into {rest} unexplained bytes per record")
|
|
165
|
+
filled = best[1]
|
|
166
|
+
notes.append(f"{len(unknown_idx)} column ID(s) unknown to {reference}: "
|
|
167
|
+
+ ", ".join(str(filled[i][0]) for i in unknown_idx) + f"; widths solved from the record size ({rec} bytes)")
|
|
168
|
+
elif rest != 0:
|
|
169
|
+
raise ValueError(f"record size {rec} does not match the known columns ({known_width} bytes)")
|
|
170
|
+
arr = np.frombuffer(data, offset=offset, count=n_points,
|
|
171
|
+
dtype=np.dtype([(f"f{i}", e[3]) for i, e in enumerate(filled)]))
|
|
172
|
+
cols, pos = [], 0
|
|
173
|
+
for i, (fid, n, u, d, kn) in enumerate(filled):
|
|
174
|
+
if fid is None:
|
|
175
|
+
continue # flags byte
|
|
176
|
+
cols.append(Col(pos, fid, n or f"unknown {fid}", u, d, kn, np.asarray(arr[f"f{i}"])))
|
|
177
|
+
pos += 1
|
|
178
|
+
return MprData(str(path), technique, n_points, rec, cols, notes)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rosetta-eclab
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Name the columns of BioLogic EC-Lab .mpr files that your parser does not know - no language model, evidence for every answer
|
|
5
|
+
Author: Nataniel Ataseven
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Repository, https://github.com/Nataniel0212/rosetta-eclab
|
|
8
|
+
Project-URL: Issues, https://github.com/Nataniel0212/rosetta-eclab/issues
|
|
9
|
+
Keywords: electrochemistry,biologic,ec-lab,mpr,battery,potentiostat,file-format
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=2.0
|
|
20
|
+
Requires-Dist: yadg==7.0.1
|
|
21
|
+
Requires-Dist: galvani==0.5.0
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Rosetta
|
|
27
|
+
|
|
28
|
+
**Rosetta names the columns of BioLogic EC-Lab `.mpr` files that your parser does not know. It shows the evidence for each answer, and it says so when the data cannot support a name.**
|
|
29
|
+
|
|
30
|
+
When the instrument vendor adds a column ID, open-source parsers such as [galvani](https://github.com/echemdata/galvani) stop reading the file until someone works out what the new column is. Rosetta reads the file anyway. It works out the byte width and storage type of each unknown column from the record size, and then identifies the physical quantity from the data. It uses no language model and no network.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ rosetta drift.mpr --self-check
|
|
34
|
+
drift.mpr
|
|
35
|
+
technique GCPL, 12057 rows, 79 bytes per record
|
|
36
|
+
1 column ID(s) unknown to galvani: 950; widths solved from the record size (79 bytes)
|
|
37
|
+
|
|
38
|
+
column ID 950 (<f8): Q charge or discharge [C or mA·h] (confidence 0.533)
|
|
39
|
+
evidence: relation: I*dt summed within half cycle: r2 1.0000, scale 0.0002778
|
|
40
|
+
also considered: dQ, step time
|
|
41
|
+
|
|
42
|
+
self-check: hid each of 11 known columns in turn; named 11, 11 correctly, 0 wrong, the rest abstained
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
*`drift.mpr` is `gcpl.mpr` from the yadg test suite, with one column ID changed to 950, an ID no parser knows (see `tests/test_cli.py`).*
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
Python 3.10 or later:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
pip install rosetta-eclab
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To run the tests or reproduce the benchmark, clone the repository instead:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
git clone https://github.com/Nataniel0212/rosetta-eclab.git
|
|
59
|
+
cd rosetta-eclab
|
|
60
|
+
pip install ".[test]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Use
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
rosetta FILE.mpr # name every column unknown to galvani (the default reference parser)
|
|
67
|
+
rosetta FILE.mpr --reference yadg # ... unknown to yadg instead
|
|
68
|
+
rosetta FILE.mpr --self-check # also hide each known column in turn and report how many come back right
|
|
69
|
+
rosetta FILE.mpr --json # machine-readable output
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Each unknown column gets one of three answers:
|
|
73
|
+
- **NAME:** an EC-Lab quantity, with its unit, a confidence and the evidence, for example the relation that holds and how well it fits.
|
|
74
|
+
- **DUPLICATE:** identical in every row to a known column.
|
|
75
|
+
- **ABSTAIN:** the data does not support a name. Rosetta prefers this to a guess.
|
|
76
|
+
|
|
77
|
+
`--self-check` shows how well Rosetta knows *your* kind of file. It hides each known column in turn and tries to name it.
|
|
78
|
+
|
|
79
|
+
## How it works
|
|
80
|
+
|
|
81
|
+
1. **Read the file** (`namer/mprfile.py`). Columns the reference parser knows are typed from its column map. For unknown IDs the byte width is solved from the record size, and the storage type is chosen by plausibility.
|
|
82
|
+
2. **Code checks first.** Constant columns and exact copies of a known column are reported as such. Names that a family of siblings makes obvious are filled in, for example the missing harmonic `|Ewe h4|`.
|
|
83
|
+
3. **Physics library** (`namer/library.py`). Every candidate quantity is tested against the file's other columns:
|
|
84
|
+
- **charges:** current × time, reset per half cycle, cycle or sequence
|
|
85
|
+
- **energies and power:** E × I and its integrals
|
|
86
|
+
- **impedance:** |Z| = √(Re² + Im²), the phase angle, Cs and Cp
|
|
87
|
+
- **clocks and step times**, and **counters** stepping by one
|
|
88
|
+
- **set points** that the measured potential or current holds to
|
|
89
|
+
|
|
90
|
+
Each test reports r² and the fitted scale, which must match the expected unit conversion. A potential can therefore not pass as a current. Fits are robust to the single glitch rows that real records have at step transitions.
|
|
91
|
+
4. **Statistics fallback** (`namer/fallback.py`). A nearest-neighbour vote over column statistics from public files. It is used only for columns that are not noise, and it is vetoed when a defining relation of the guessed quantity fails in the file.
|
|
92
|
+
5. **Abstain** when nothing above supports a name.
|
|
93
|
+
|
|
94
|
+
## How accurate it is
|
|
95
|
+
|
|
96
|
+
The numbers come from five hold-out sets of public EC-Lab files from Zenodo, Battery Data Format and navani: 373 real columns and 95 decoys. None of these files were used during development. Each set was frozen before the namer version that ran on it, and each was run once (`results/open_runs.md`).
|
|
97
|
+
|
|
98
|
+
In every task one real column is hidden and must be named from the data. There are also decoys, where the right answer is not to name the column:
|
|
99
|
+
- noise and random integers
|
|
100
|
+
- *out-of-vocabulary* columns: real columns whose name the tool is not allowed to use, the way a brand-new quantity would look
|
|
101
|
+
|
|
102
|
+
| | Rosetta (no model) | Claude Opus 5.5 | best code-only baseline |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| Real columns named correctly | 350/373 (93.8 %) | 359/373 (96.2 %) | 77–100 % per hold-out¹ |
|
|
105
|
+
| Wrong names on real columns | **5** | 10 | – |
|
|
106
|
+
| Decoys named (should abstain) | **11/95** | 16/95 | 95/95 |
|
|
107
|
+
| Last hold-out (70 columns) | **70/70, 0 wrong, 1 decoy named** | 69/70, 1 wrong, 2 decoys named | 78.6 % |
|
|
108
|
+
|
|
109
|
+
¹ There are two code-only baselines. A technique prior names the column that is typical for this technique and missing here. A nearest-neighbour vote compares the column with labelled columns from other files. Both are strong when files have standard column sets, but they name every decoy and cannot name a genuinely new column.
|
|
110
|
+
|
|
111
|
+
The namer improved between hold-outs, so the total includes earlier versions. The history, including the language-model experiments that came first, is in [`docs/history.md`](https://github.com/Nataniel0212/rosetta-eclab/blob/main/docs/history.md).
|
|
112
|
+
|
|
113
|
+
### Limitations
|
|
114
|
+
|
|
115
|
+
- **Values EC-Lab computes after the fact** cannot be reconstructed reliably. For example, a capacitance is held per segment.
|
|
116
|
+
- **Noise-like DC currents** in impedance records, and **external analog inputs** with no sibling column, usually get ABSTAIN.
|
|
117
|
+
- **Two quantities with the same definition in a file** cannot be told apart from the data. In galvanostatic cycling, for example, `dQ` and `Q charge or discharge` reset at the same points. A prior over how common each name is for the technique decides between them, and the alternative is shown.
|
|
118
|
+
- **Supported formats:** EC-Lab data-module versions 2, 3, 10 and 11. Very old files (version 0) are not supported yet.
|
|
119
|
+
|
|
120
|
+
## Reproduce the benchmark
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
python scripts/fetch_testdata.py # development corpus (yadg + galvani test files)
|
|
124
|
+
python scripts/fetch_holdout.py holdout5 # any hold-out; see DATA_SOURCES.md
|
|
125
|
+
python -m spike2.tasks # v2 benchmark tasks (hashes in prereg2/tasks_manifest.json)
|
|
126
|
+
python -m holdout.build holdout5 # hold-out tasks (hash in holdout/manifest_holdout5.json)
|
|
127
|
+
python -m namer.evaluate --split holdout5 # run the namer
|
|
128
|
+
python -m spike2.baselines --split holdout5 # code-only baselines
|
|
129
|
+
python -m bench.claude_cli --split holdout5 --score rules # score (Claude runs need the Claude Code CLI)
|
|
130
|
+
python -m namer.golden check # regression check: every answer must match tests/golden
|
|
131
|
+
pytest tests
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Data and licence
|
|
135
|
+
|
|
136
|
+
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) and [galvani](https://github.com/echemdata/galvani), both GPL. Measurement files are downloaded at run time and never committed. Sources, licences and attributions are listed in [`DATA_SOURCES.md`](https://github.com/Nataniel0212/rosetta-eclab/blob/main/DATA_SOURCES.md). Rosetta is not affiliated with BioLogic.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
rosetta_eclab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
rosetta_eclab/cli.py,sha256=oe_hdjIQFOtHIkXqYqeVf2ijG1HJVHE-F5OJnqy5E6k,5087
|
|
3
|
+
rosetta_eclab/debug.py,sha256=ddsJm2aV-nUktFlIAOym7yXO0P-olMCv2T1aRObSzpk,3461
|
|
4
|
+
rosetta_eclab/engine.py,sha256=YVQQ4zAoxJn193212Qe5FGYFUvh8IjP8EjE9u9Md5hU,9006
|
|
5
|
+
rosetta_eclab/evaluate.py,sha256=ZoTh1LIrz-RJ5G1wiZ3flxU-ODPJXnV-BfCCKHkHaIM,9031
|
|
6
|
+
rosetta_eclab/fallback.py,sha256=enxXyy-IJGm7zwgWrn-jvZsRPvC6rvcuUQg7rw1E9LQ,5935
|
|
7
|
+
rosetta_eclab/golden.py,sha256=_oONIABpdWFHoHXW--0uEh-UwSi17OzEe5lawSJQZDU,2947
|
|
8
|
+
rosetta_eclab/library.py,sha256=3LG7fOJYXRNgEtsvxQv3WuzgKVJCe3YkzngPMqSpQkM,35677
|
|
9
|
+
rosetta_eclab/misses.py,sha256=XBw6yfCDFIkXV5GMtJNnyKqqqV7gESjjm1xIYd_qUKE,1550
|
|
10
|
+
rosetta_eclab/mprfile.py,sha256=dDEVZe3aPzzqEKH10q44Re2y3wYnEkUwzNkRoF-jifU,7471
|
|
11
|
+
rosetta_eclab/data/stats.json,sha256=MNp2xOiOZHCbmGfdACBDV5zWOoatIH7pKp3Be8rqtK8,28652
|
|
12
|
+
rosetta_eclab/data/stats_features.npy,sha256=-bCgLTO5DNiJLXy-mXFhuRpX_X9jY1eTYk8C-h1hfkE,73728
|
|
13
|
+
rosetta_eclab-0.1.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
14
|
+
spike2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
spike2/agent.py,sha256=FtjyJbzynSidjfOsKit9fH-zWsbJ-Q2LwCX1SAVGPkE,17325
|
|
16
|
+
spike2/api_check.py,sha256=EkyVF6UJwK-_GIPRI6Zi113McMKhMGbtHBTobAwnCt4,5725
|
|
17
|
+
spike2/baselines.py,sha256=P6gpXGFdZVAQMQDBlgi01luTXUd63_bvlmI1d4n3Rgs,6530
|
|
18
|
+
spike2/corpus.py,sha256=iH-WIZf6mnjO1EHKDc2esurJTeIAu-J9L1wV3m_gUJo,6081
|
|
19
|
+
spike2/dsl.py,sha256=jlogDcHMFEuA3o-5ex-DhFXi55huIZ0hOOviNIeZ2Lw,6401
|
|
20
|
+
spike2/elim.py,sha256=hkPjy0xHEwELjpU4UpICuhEL2fOa5c6BRNnl5tPljXI,1971
|
|
21
|
+
spike2/features.py,sha256=BR_5zLGx85rMkWSoyRgnXjK4pC7VkM0fvhWkyHz8arY,4752
|
|
22
|
+
spike2/lock.py,sha256=tyg354VnCjPzw6zvluWDOq--MZa1YUS2X42z76HVe9s,2708
|
|
23
|
+
spike2/run.py,sha256=PtJszxAo2QiZfidjM6AtgQg5zG5bFPEPUiB4Pvup4cs,10407
|
|
24
|
+
spike2/score.py,sha256=ypoeiUXOSW_t9FZdQQtPa1t4zgMWDnh1FEXEJe02Va0,10869
|
|
25
|
+
spike2/tasks.py,sha256=bWm6QcEiUq5K8F4lDG4i4OKhw0H7RyLh6BikY6Ns_vQ,15805
|
|
26
|
+
spike2/vocab.py,sha256=MtcNoLo5aLS-uUXiUJoih74Dvy6CpOlKX0cqCckSuJ8,4537
|
|
27
|
+
rosetta_eclab-0.1.1.dist-info/METADATA,sha256=5Ti6e2N7nppDfmpFyRwM77zj4vMvZr3SqBfy9VWw3qw,8329
|
|
28
|
+
rosetta_eclab-0.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
29
|
+
rosetta_eclab-0.1.1.dist-info/entry_points.txt,sha256=3XKxa49VWnGYZBrFuzpqS0qMW5hKAkBgyIsk2wfgSxk,51
|
|
30
|
+
rosetta_eclab-0.1.1.dist-info/top_level.txt,sha256=8IvVzyW3bwuHVOAFu1n8uGLIn_M2F3jNORuvQH6p-1I,21
|
|
31
|
+
rosetta_eclab-0.1.1.dist-info/RECORD,,
|