rocket-engine 0.0.2b1__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.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Meow v1 normalizer: v0 parse output to a ResultBundle.
|
|
2
|
+
|
|
3
|
+
Maps the ``run_em`` parse shape (``measurements[]`` with ratio
|
|
4
|
+
value, target, margin, and pass/fail status plus convergence
|
|
5
|
+
values) onto a typed metrics table, a bound view, measurements,
|
|
6
|
+
and findings. Pure function of the parsed output: no I/O, no
|
|
7
|
+
tool calls, deterministic ids derived from the run id. PASS
|
|
8
|
+
measurements keep value, target, margin, and status.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
TASK = "run_em"
|
|
12
|
+
|
|
13
|
+
_DIAG_CAP = 240
|
|
14
|
+
|
|
15
|
+
_SEVERITY = {"error": "error", "warning": "warning"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _cap(text):
|
|
19
|
+
"""Cap one diagnostic string at the bundle prose limit.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
text: Raw diagnostic text, possibly unbounded tool prose.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The text unchanged when short, else truncated with an
|
|
26
|
+
ellipsis marker.
|
|
27
|
+
"""
|
|
28
|
+
text = text if isinstance(text, str) else str(text)
|
|
29
|
+
if len(text) <= _DIAG_CAP:
|
|
30
|
+
return text
|
|
31
|
+
return text[: _DIAG_CAP - 3] + "..."
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _quantity(value, unit):
|
|
35
|
+
"""Build one v1 Quantity, mapping unitless to dimensionless.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: Numeric value.
|
|
39
|
+
unit: Unit string, possibly empty for unitless measures.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Quantity mapping with an explicit unit.
|
|
43
|
+
"""
|
|
44
|
+
return {"value": float(value), "unit": unit if unit else "1"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _limit(item):
|
|
48
|
+
"""Pick the binding target bound for one measurement.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
item: v0 measurement with a ``target`` min/max mapping.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
The bound closest to the measured value (the margin
|
|
55
|
+
binding bound), or None when the target is empty.
|
|
56
|
+
"""
|
|
57
|
+
target = item.get("target") or {}
|
|
58
|
+
scored = []
|
|
59
|
+
if target.get("min") is not None:
|
|
60
|
+
scored.append((float(item["value"]) - float(target["min"]),
|
|
61
|
+
float(target["min"])))
|
|
62
|
+
if target.get("max") is not None:
|
|
63
|
+
scored.append((float(target["max"]) - float(item["value"]),
|
|
64
|
+
float(target["max"])))
|
|
65
|
+
if not scored:
|
|
66
|
+
return None
|
|
67
|
+
return min(scored)[1]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _provenance(project_id, run_id, tools, provenance):
|
|
71
|
+
"""Build ResultProvenance, defaulting to a placeholder closure.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
project_id: Project the run belongs to.
|
|
75
|
+
run_id: Run the bundle describes.
|
|
76
|
+
tools: Tool ingredient mappings (id, version, digest?).
|
|
77
|
+
provenance: Full provenance mapping when the caller has
|
|
78
|
+
one; a deterministic placeholder otherwise.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Provenance mapping valid against the v1 schema.
|
|
82
|
+
"""
|
|
83
|
+
if provenance is not None:
|
|
84
|
+
return provenance
|
|
85
|
+
ref = {"scope": "project", "id": project_id, "revision": "r1"}
|
|
86
|
+
return {
|
|
87
|
+
"snapshot": dict(ref),
|
|
88
|
+
"input_lock": dict(ref),
|
|
89
|
+
"closure_hash": "0" * 64,
|
|
90
|
+
"tools": [
|
|
91
|
+
{
|
|
92
|
+
"id": item["id"],
|
|
93
|
+
"version": item.get("version", ""),
|
|
94
|
+
"digest": item.get("digest", "0" * 64),
|
|
95
|
+
}
|
|
96
|
+
for item in tools
|
|
97
|
+
],
|
|
98
|
+
"parents": [],
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def normalize(parsed, *, run_id, project_id="project", target=None,
|
|
103
|
+
tools=(), provenance=None):
|
|
104
|
+
"""Normalize one meow parse output to a v1 ResultBundle.
|
|
105
|
+
|
|
106
|
+
Metric rows become a table dataset plus a bound table view;
|
|
107
|
+
violations become findings with capped messages. PASS
|
|
108
|
+
measurements keep value, target, margin, and status. A
|
|
109
|
+
malformed device (no metrics) reports not_converged with its
|
|
110
|
+
error finding.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
parsed: v0 parse mapping with ``measurements[]`` and
|
|
114
|
+
``violations[]``.
|
|
115
|
+
run_id: Run the bundle describes (anchors all ids).
|
|
116
|
+
project_id: Project the run belongs to.
|
|
117
|
+
target: v1 target mapping; project kind by default.
|
|
118
|
+
tools: Tool ingredient mappings for provenance.
|
|
119
|
+
provenance: Full provenance mapping when the caller has
|
|
120
|
+
one; a deterministic placeholder otherwise.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Schema-valid ResultBundle mapping.
|
|
124
|
+
"""
|
|
125
|
+
measurements_in = parsed.get("measurements") or []
|
|
126
|
+
violations = parsed.get("violations") or []
|
|
127
|
+
table_id = f"{run_id}:dataset:metrics"
|
|
128
|
+
datasets = [
|
|
129
|
+
{
|
|
130
|
+
"id": table_id,
|
|
131
|
+
"kind": "table",
|
|
132
|
+
"row_count": len(measurements_in),
|
|
133
|
+
"columns": [
|
|
134
|
+
{
|
|
135
|
+
"id": f"{table_id}:name",
|
|
136
|
+
"name": "name",
|
|
137
|
+
"dtype": "utf8",
|
|
138
|
+
"unit": "1",
|
|
139
|
+
"data": [item["name"] for item in measurements_in],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": f"{table_id}:value",
|
|
143
|
+
"name": "value",
|
|
144
|
+
"dtype": "float64",
|
|
145
|
+
"unit": "1",
|
|
146
|
+
"data": [float(item["value"]) for item in measurements_in],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": f"{table_id}:margin",
|
|
150
|
+
"name": "margin",
|
|
151
|
+
"dtype": "float64",
|
|
152
|
+
"unit": "1",
|
|
153
|
+
"data": [float(item["margin"]) for item in measurements_in],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": f"{table_id}:status",
|
|
157
|
+
"name": "status",
|
|
158
|
+
"dtype": "utf8",
|
|
159
|
+
"unit": "1",
|
|
160
|
+
"data": [item["status"] for item in measurements_in],
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
"conditions": [],
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
views = [
|
|
167
|
+
{
|
|
168
|
+
"id": f"{run_id}:view:metrics",
|
|
169
|
+
"kind": "table",
|
|
170
|
+
"dataset_id": table_id,
|
|
171
|
+
"name": "em metrics",
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
measurements = []
|
|
175
|
+
for item in measurements_in:
|
|
176
|
+
entry = {
|
|
177
|
+
"name": item["name"],
|
|
178
|
+
"value": _quantity(item["value"], item.get("unit", "")),
|
|
179
|
+
"margin": _quantity(item["margin"], item.get("unit", "")),
|
|
180
|
+
"status": "PASS" if item["status"] == "pass" else "FAIL",
|
|
181
|
+
}
|
|
182
|
+
bound = _limit(item)
|
|
183
|
+
if bound is not None:
|
|
184
|
+
entry["limit"] = _quantity(bound, item.get("unit", ""))
|
|
185
|
+
measurements.append(entry)
|
|
186
|
+
findings = [
|
|
187
|
+
{
|
|
188
|
+
"id": f"{run_id}:finding:{index}",
|
|
189
|
+
"severity": _SEVERITY.get(v.get("severity"), "info"),
|
|
190
|
+
"code": v.get("rule", "device"),
|
|
191
|
+
"message": _cap(v.get("message", "")),
|
|
192
|
+
}
|
|
193
|
+
for index, v in enumerate(violations)
|
|
194
|
+
]
|
|
195
|
+
converged = not any(
|
|
196
|
+
v.get("rule") in ("device", "s_param", "convergence")
|
|
197
|
+
for v in violations
|
|
198
|
+
)
|
|
199
|
+
return {
|
|
200
|
+
"schema": "rocketeda/results/v1",
|
|
201
|
+
"ref": {"scope": "project", "id": run_id, "revision": "r1"},
|
|
202
|
+
"run_id": run_id,
|
|
203
|
+
"task": TASK,
|
|
204
|
+
"target": dict(target)
|
|
205
|
+
if target is not None
|
|
206
|
+
else {"project_id": project_id, "kind": "project"},
|
|
207
|
+
"state": "complete",
|
|
208
|
+
"freshness": "current",
|
|
209
|
+
"convergence": "converged" if converged else "not_converged",
|
|
210
|
+
"provenance": _provenance(project_id, run_id, tools, provenance),
|
|
211
|
+
"datasets": datasets,
|
|
212
|
+
"views": views,
|
|
213
|
+
"measurements": measurements,
|
|
214
|
+
"findings": findings,
|
|
215
|
+
"artifacts": [],
|
|
216
|
+
"logs": [],
|
|
217
|
+
"gaps": [],
|
|
218
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Mutation adapter: run_mutation with the mcy provider."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
import sqlite3
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ...execution.errors import ExecutionError
|
|
10
|
+
from ...interfaces import ToolAdapter
|
|
11
|
+
from ..errors import AdapterError
|
|
12
|
+
|
|
13
|
+
OPERATION = "run_mutation"
|
|
14
|
+
_TOOL = "mcy"
|
|
15
|
+
_DB = "database/db.sqlite3"
|
|
16
|
+
_OUTPUT_RE = re.compile(r"^(\d+)\s+(\S+)\s*$")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _parse_output_txt(text):
|
|
20
|
+
"""Parse one output.txt streaming verdict file.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
text: output.txt text with `<idx> <RESULT>` per line.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
List of {mutation_id, result} rows.
|
|
27
|
+
"""
|
|
28
|
+
rows = []
|
|
29
|
+
for line in (text or "").splitlines():
|
|
30
|
+
match = _OUTPUT_RE.match(line.strip())
|
|
31
|
+
if match is None:
|
|
32
|
+
continue
|
|
33
|
+
rows.append({"mutation_id": int(match.group(1)),
|
|
34
|
+
"result": match.group(2)})
|
|
35
|
+
return rows
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class McyAdapter(ToolAdapter):
|
|
39
|
+
"""run_mutation via mcy: sqlite DB plus output.txt streaming.
|
|
40
|
+
|
|
41
|
+
Serves the run_mutation operation with tool `mcy`. Primary
|
|
42
|
+
results come from database/db.sqlite3 via stdlib sqlite3;
|
|
43
|
+
output.txt `<idx> <RESULT>` rows stream per-task verdicts
|
|
44
|
+
before DB commit. Never scrapes list/source/status stdout.
|
|
45
|
+
|
|
46
|
+
Job keys:
|
|
47
|
+
project (required): Mcy project dir with database/.
|
|
48
|
+
test: Test name for run scoping.
|
|
49
|
+
workdir: Run directory (default ".").
|
|
50
|
+
timeout_s: Budget in seconds (default 600).
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
runner: Optional isolated runner; direct subprocess when None.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, runner=None):
|
|
57
|
+
self._runner = runner
|
|
58
|
+
|
|
59
|
+
parse_output_txt = staticmethod(_parse_output_txt)
|
|
60
|
+
|
|
61
|
+
def capabilities(self, target):
|
|
62
|
+
"""Report the served operation and tool.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
target: Target the capabilities are reported for.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Capability mapping with operations and tool.
|
|
69
|
+
"""
|
|
70
|
+
return {"operations": [OPERATION], "tool": _TOOL, "db": _DB}
|
|
71
|
+
|
|
72
|
+
def preflight(self, job):
|
|
73
|
+
"""Check operation, project dir, and mcy presence.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
job: Solver job with operation run_mutation plus project.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Checked mapping with project, binary, and timeout.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
AdapterError: unsupported_operation, invalid_reference
|
|
83
|
+
for a missing project, missing_dependency when mcy
|
|
84
|
+
is absent (BLOCKED-needs-host).
|
|
85
|
+
"""
|
|
86
|
+
if job.get("operation") != OPERATION:
|
|
87
|
+
raise AdapterError(
|
|
88
|
+
"unsupported_operation",
|
|
89
|
+
f"Mcy handles {OPERATION}, got {job.get('operation')}.")
|
|
90
|
+
project = job.get("project")
|
|
91
|
+
if not project or not Path(project).is_dir():
|
|
92
|
+
raise AdapterError(
|
|
93
|
+
"invalid_reference", f"Mcy project not found: {project}.")
|
|
94
|
+
binary = shutil.which(_TOOL)
|
|
95
|
+
if binary is None:
|
|
96
|
+
raise AdapterError(
|
|
97
|
+
"missing_dependency",
|
|
98
|
+
"BLOCKED-needs-host: mcy not on PATH (oss-cad-suite).")
|
|
99
|
+
timeout = job.get("timeout_s", 600)
|
|
100
|
+
if (not isinstance(timeout, (int, float))
|
|
101
|
+
or isinstance(timeout, bool) or not timeout > 0):
|
|
102
|
+
raise AdapterError("invalid_command", f"Bad timeout: {timeout!r}.")
|
|
103
|
+
checked = {"project": str(project), "binary": binary,
|
|
104
|
+
"timeout_s": timeout, "test": job.get("test")}
|
|
105
|
+
if self._runner is not None:
|
|
106
|
+
try:
|
|
107
|
+
self._runner.preflight()
|
|
108
|
+
except ExecutionError as exc:
|
|
109
|
+
raise AdapterError(exc.code, str(exc)) from exc
|
|
110
|
+
return checked
|
|
111
|
+
|
|
112
|
+
def execute(self, job):
|
|
113
|
+
"""Run mcy on the project under a timeout.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
job: Solver job with operation run_mutation plus project.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Native output with exit_code, stdout/stderr, results,
|
|
120
|
+
and db file.
|
|
121
|
+
"""
|
|
122
|
+
checked = self.preflight(job)
|
|
123
|
+
workdir = job.get("workdir", checked["project"])
|
|
124
|
+
if not Path(workdir).is_dir():
|
|
125
|
+
raise AdapterError(
|
|
126
|
+
"invalid_reference", f"Workdir not found: {workdir}.")
|
|
127
|
+
argv = [checked["binary"], "run"]
|
|
128
|
+
if checked["test"]:
|
|
129
|
+
argv.extend(["--test", str(checked["test"])])
|
|
130
|
+
try:
|
|
131
|
+
proc = subprocess.run(argv, capture_output=True, text=True,
|
|
132
|
+
timeout=checked["timeout_s"], cwd=workdir)
|
|
133
|
+
except subprocess.TimeoutExpired as exc:
|
|
134
|
+
raise AdapterError(
|
|
135
|
+
"runner_unavailable", "Mcy run timed out.") from exc
|
|
136
|
+
results = self.read_db(Path(workdir) / _DB)
|
|
137
|
+
return {"exit_code": proc.returncode, "stdout": proc.stdout,
|
|
138
|
+
"stderr": proc.stderr, "results": results,
|
|
139
|
+
"db_file": _DB}
|
|
140
|
+
|
|
141
|
+
def parse(self, native_output):
|
|
142
|
+
"""Parse DB results to coverage plus uncovered violations.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
native_output: Native output with the results rows.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
{total, violations[], coverage, source}.
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
AdapterError: runner_unavailable with no results.
|
|
152
|
+
"""
|
|
153
|
+
results = native_output.get("results")
|
|
154
|
+
if not isinstance(results, list) or not results:
|
|
155
|
+
raise AdapterError(
|
|
156
|
+
"runner_unavailable", "Mcy output has no results.")
|
|
157
|
+
uncovered = [r for r in results
|
|
158
|
+
if str(r.get("result", "")).upper() != "COVERED"
|
|
159
|
+
and str(r.get("result", "")).upper() != "PASS"]
|
|
160
|
+
coverage = ((len(results) - len(uncovered)) / len(results)
|
|
161
|
+
if results else 0.0)
|
|
162
|
+
violations = [{"rule": "mutation_uncovered", "severity": "error",
|
|
163
|
+
"message": f"Mutation {r.get('mutation_id')}"
|
|
164
|
+
f" {r.get('result')}."}
|
|
165
|
+
for r in uncovered]
|
|
166
|
+
return {"total": len(violations), "violations": violations,
|
|
167
|
+
"coverage": coverage, "source": "db",
|
|
168
|
+
"db_file": native_output.get("db_file", _DB)}
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def read_db(path):
|
|
172
|
+
"""Read cached results from one mcy sqlite DB.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
path: database/db.sqlite3 path (missing reads as empty).
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Results rows (empty when the DB is absent).
|
|
179
|
+
"""
|
|
180
|
+
if not Path(path).is_file():
|
|
181
|
+
return []
|
|
182
|
+
try:
|
|
183
|
+
conn = sqlite3.connect(str(path))
|
|
184
|
+
try:
|
|
185
|
+
rows = conn.execute(
|
|
186
|
+
"SELECT mutation_id, test, result FROM results").fetchall()
|
|
187
|
+
finally:
|
|
188
|
+
conn.close()
|
|
189
|
+
except Exception:
|
|
190
|
+
return []
|
|
191
|
+
return [{"mutation_id": r[0], "test": r[1], "result": r[2]}
|
|
192
|
+
for r in rows]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ngspice adapter
|
|
2
|
+
|
|
3
|
+
`run_spice` behind the ToolAdapter port (SIM-SKY130).
|
|
4
|
+
|
|
5
|
+
Job: `sources` (netlists, `.end` lines stripped), `measurements`
|
|
6
|
+
(`name`/`expr`/`min`/`max`/`unit`), `analyses` (default `op`),
|
|
7
|
+
`options` (default `wnflag=1`), `params` (`.param` lines),
|
|
8
|
+
`corner` (default `tt`), `temp_c` (default 27), optional `pdk`
|
|
9
|
+
(`id`/`revision`/`model_rel`/`engine_root`, resolved through the
|
|
10
|
+
PDK store so a wrong rev fails loudly before any sim).
|
|
11
|
+
|
|
12
|
+
Staging: `ngspice_deck.spice` (preamble + bodies + generated
|
|
13
|
+
`.control` with `let`/`print meas_*`) plus `.spiceinit`
|
|
14
|
+
(neutral base lines plus the lane profile extras, see
|
|
15
|
+
`../profile.py` and `docs/pdk/<lane>/profile.yaml`). Runs
|
|
16
|
+
`ngspice -b -o ngspice.log ngspice_deck.spice`.
|
|
17
|
+
|
|
18
|
+
Simulator needs are profile-driven (PDK-PROFILE pattern): the
|
|
19
|
+
default model relpath and `.spiceinit` extras come from the PDK
|
|
20
|
+
lane profile, never from adapter special-cases. An explicit
|
|
21
|
+
`model_rel` in the job pin always wins; a PDK with no sim
|
|
22
|
+
profile and no explicit `model_rel` fails loudly
|
|
23
|
+
(`invalid_command`, never a guessed relpath).
|
|
24
|
+
|
|
25
|
+
Parse: `meas_* = value` lines evaluated against bounds (SAX
|
|
26
|
+
`measurements` shape); a sim yielding no values synthesizes
|
|
27
|
+
`sim_fail` violations carrying ngspice's own error text. The
|
|
28
|
+
verdict always comes from parsed output, never the exit code.
|