equiframe-benchmark 0.12.4__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.
- equiframe_benchmark/PROTOCOL_V2.md +172 -0
- equiframe_benchmark/__init__.py +284 -0
- equiframe_benchmark/__main__.py +3 -0
- equiframe_benchmark/_scale_workload.py +375 -0
- equiframe_benchmark/calibration_types.py +112 -0
- equiframe_benchmark/cli.py +196 -0
- equiframe_benchmark/commands.py +149 -0
- equiframe_benchmark/commands_scale.py +38 -0
- equiframe_benchmark/commands_step10.py +275 -0
- equiframe_benchmark/commands_v2.py +206 -0
- equiframe_benchmark/cost_table.py +62 -0
- equiframe_benchmark/costs.py +788 -0
- equiframe_benchmark/distribution.py +932 -0
- equiframe_benchmark/evaluation.py +614 -0
- equiframe_benchmark/evaluation_v2.py +428 -0
- equiframe_benchmark/fixtures.py +299 -0
- equiframe_benchmark/freeze.py +869 -0
- equiframe_benchmark/freeze_v2.py +412 -0
- equiframe_benchmark/integration.py +655 -0
- equiframe_benchmark/io.py +29 -0
- equiframe_benchmark/models.py +313 -0
- equiframe_benchmark/models_v2.py +13 -0
- equiframe_benchmark/mutations.py +34 -0
- equiframe_benchmark/oracle.py +264 -0
- equiframe_benchmark/pairwise.py +248 -0
- equiframe_benchmark/pilot.py +254 -0
- equiframe_benchmark/py.typed +0 -0
- equiframe_benchmark/reconciliation.py +294 -0
- equiframe_benchmark/reproduction.py +542 -0
- equiframe_benchmark/scale_io.py +19 -0
- equiframe_benchmark/scale_process.py +1077 -0
- equiframe_benchmark/scale_types.py +120 -0
- equiframe_benchmark/scaling.py +529 -0
- equiframe_benchmark/schemas/protocol-v2/CommonSupportV2-2.0.schema.json +327 -0
- equiframe_benchmark/schemas/protocol-v2/ContrastSpecV2-2.0.schema.json +37 -0
- equiframe_benchmark/schemas/protocol-v2/EvaluationConfigV2-2.0.schema.json +184 -0
- equiframe_benchmark/schemas/protocol-v2/EvaluationReportV2-2.0.schema.json +1237 -0
- equiframe_benchmark/schemas/protocol-v2/EvaluationStratumV2-2.0.schema.json +747 -0
- equiframe_benchmark/schemas/protocol-v2/FreezeDefinitionV2-2.0.schema.json +5951 -0
- equiframe_benchmark/schemas/protocol-v2/FreezeManifestV2-2.0.schema.json +6222 -0
- equiframe_benchmark/schemas/protocol-v2/PrincipalContrastV2-2.0.schema.json +578 -0
- equiframe_benchmark/schemas/protocol-v2/ProtocolReviewV2-2.0.schema.json +56 -0
- equiframe_benchmark/schemas/protocol-v2/SignedMetricV2-2.0.schema.json +51 -0
- equiframe_benchmark/schemas/protocol-v2/StudyCaseResultV2-2.0.schema.json +4881 -0
- equiframe_benchmark/schemas/protocol-v2/StudyResultV2-2.0.schema.json +8370 -0
- equiframe_benchmark/schemas/protocol-v2/TableExportV2-2.0.schema.json +75 -0
- equiframe_benchmark/selection.py +178 -0
- equiframe_benchmark/snapshot.py +24 -0
- equiframe_benchmark/splits.py +92 -0
- equiframe_benchmark/study.py +526 -0
- equiframe_benchmark/study_bundle.py +379 -0
- equiframe_benchmark/study_bundle_v2.py +388 -0
- equiframe_benchmark/study_v2.py +544 -0
- equiframe_benchmark/tables.py +347 -0
- equiframe_benchmark/tables_v2.py +340 -0
- equiframe_benchmark/training_types.py +52 -0
- equiframe_benchmark-0.12.4.dist-info/METADATA +343 -0
- equiframe_benchmark-0.12.4.dist-info/RECORD +62 -0
- equiframe_benchmark-0.12.4.dist-info/WHEEL +4 -0
- equiframe_benchmark-0.12.4.dist-info/entry_points.txt +3 -0
- equiframe_benchmark-0.12.4.dist-info/licenses/LICENSE +21 -0
- equiframe_benchmark-0.12.4.dist-info/licenses/THIRD_PARTY_NOTICES.md +8 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Five manually specified synthetic fixtures; adapted from the step 2 native examples."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from datetime import date, datetime
|
|
8
|
+
from decimal import Decimal
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
import equiframe as ef
|
|
12
|
+
import pyarrow as pa
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Mapping
|
|
16
|
+
|
|
17
|
+
import pandas as pd
|
|
18
|
+
import polars as pl
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def column(name: str, dtype: ef.LogicalType) -> ef.ColumnSpec:
|
|
22
|
+
numeric = isinstance(dtype, (ef.IntegerType, ef.FloatType, ef.DecimalType))
|
|
23
|
+
temporal: ef.DatePolicy | ef.TimestampPolicy | None = None
|
|
24
|
+
if isinstance(dtype, ef.DateType):
|
|
25
|
+
temporal = ef.DatePolicy(closed="both")
|
|
26
|
+
elif isinstance(dtype, ef.TimestampType):
|
|
27
|
+
temporal = ef.TimestampPolicy(
|
|
28
|
+
granularity=dtype.unit,
|
|
29
|
+
timezone=dtype.timezone,
|
|
30
|
+
naive="reject" if dtype.timezone else "preserve",
|
|
31
|
+
comparison="instant" if dtype.timezone else "wall_time",
|
|
32
|
+
ambiguous="reject",
|
|
33
|
+
closed="both",
|
|
34
|
+
)
|
|
35
|
+
return ef.ColumnSpec(
|
|
36
|
+
name=name,
|
|
37
|
+
logical_type=dtype,
|
|
38
|
+
nullable=True,
|
|
39
|
+
null_policy=ef.NullPolicy(representations=("null",)),
|
|
40
|
+
nan_policy=ef.NaNPolicy(handling="distinct") if isinstance(dtype, ef.FloatType) else None,
|
|
41
|
+
numeric_policy=ef.NumericPolicy(comparison="exact", unit="synthetic_units")
|
|
42
|
+
if numeric
|
|
43
|
+
else None,
|
|
44
|
+
temporal_policy=temporal,
|
|
45
|
+
allowed_values=(),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def schema(columns: tuple[ef.ColumnSpec, ...]) -> ef.TableSchema:
|
|
50
|
+
return ef.TableSchema(
|
|
51
|
+
columns=columns, keys=(), multiplicity="preserve", order=ef.IgnoredOrder()
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def contract(
|
|
56
|
+
family: ef.Family,
|
|
57
|
+
inputs: dict[str, tuple[ef.ColumnSpec, ...]],
|
|
58
|
+
output: tuple[ef.ColumnSpec, ...],
|
|
59
|
+
) -> ef.Contract:
|
|
60
|
+
return ef.Contract(
|
|
61
|
+
schema_version="1.0",
|
|
62
|
+
contract_id="synthetic." + family,
|
|
63
|
+
revision=1,
|
|
64
|
+
family=family,
|
|
65
|
+
inputs=tuple(ef.TableSpec(name=n, schema_spec=schema(c)) for n, c in inputs.items()),
|
|
66
|
+
output=ef.TableSpec(name="result", schema_spec=schema(output)),
|
|
67
|
+
preconditions=(),
|
|
68
|
+
rules=(
|
|
69
|
+
ef.RuleSpec(
|
|
70
|
+
rule_id="output_schema",
|
|
71
|
+
revision=1,
|
|
72
|
+
required=True,
|
|
73
|
+
severity="error",
|
|
74
|
+
applicability=(),
|
|
75
|
+
specification=ef.SchemaCheck(table="result"),
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class Case:
|
|
83
|
+
contract: ef.Contract
|
|
84
|
+
inputs: dict[str, pa.Table]
|
|
85
|
+
expected: list[dict[str, object]]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def cases() -> list[Case]:
|
|
89
|
+
text = ef.StringType()
|
|
90
|
+
integer = ef.IntegerType(bits=64, signed=True)
|
|
91
|
+
decimal = ef.DecimalType(precision=18, scale=2)
|
|
92
|
+
retail = (
|
|
93
|
+
column("id", text),
|
|
94
|
+
column("amount", decimal),
|
|
95
|
+
column("measurement", ef.FloatType(bits=64)),
|
|
96
|
+
)
|
|
97
|
+
sales = pa.table(
|
|
98
|
+
{
|
|
99
|
+
"id": pa.array(["001", "001", "002", "003"], type=pa.string()),
|
|
100
|
+
"amount": pa.array(
|
|
101
|
+
[Decimal("10.20"), Decimal("10.20"), None, Decimal("0.00")],
|
|
102
|
+
type=pa.decimal128(18, 2),
|
|
103
|
+
),
|
|
104
|
+
"measurement": pa.array(
|
|
105
|
+
[None, float("nan"), 0.0, 2.5], type=pa.float64(), from_pandas=False
|
|
106
|
+
),
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
observed: list[dict[str, object]] = [
|
|
110
|
+
{"id": "001", "amount": Decimal("10.20"), "measurement": None},
|
|
111
|
+
{"id": "001", "amount": Decimal("10.20"), "measurement": float("nan")},
|
|
112
|
+
{"id": "002", "amount": None, "measurement": 0.0},
|
|
113
|
+
{"id": "003", "amount": Decimal("0.00"), "measurement": 2.5},
|
|
114
|
+
]
|
|
115
|
+
orders = (column("id", text), column("product_id", text))
|
|
116
|
+
products = (column("product_id", text), column("category", text))
|
|
117
|
+
joined = (*orders, column("category", text))
|
|
118
|
+
grouped = (column("warehouse", text), column("amount", decimal), column("id", text))
|
|
119
|
+
totals = (
|
|
120
|
+
column("warehouse", text),
|
|
121
|
+
column("total", ef.DecimalType(precision=38, scale=2)),
|
|
122
|
+
column("rows", integer),
|
|
123
|
+
)
|
|
124
|
+
stamps = (
|
|
125
|
+
column("sensor_id", text),
|
|
126
|
+
column("recorded_at", ef.TimestampType(unit="us", timezone="UTC")),
|
|
127
|
+
)
|
|
128
|
+
days = (column("sensor_id", text), column("day", ef.DateType()))
|
|
129
|
+
return [
|
|
130
|
+
Case(contract("nulls_types", {"items": retail}, retail), {"items": sales}, observed),
|
|
131
|
+
Case(contract("duplicates_order", {"items": retail}, retail), {"items": sales}, observed),
|
|
132
|
+
Case(
|
|
133
|
+
contract("joins", {"orders": orders, "products": products}, joined),
|
|
134
|
+
{
|
|
135
|
+
"orders": pa.table({"id": ["001", "002"], "product_id": ["A", "Z"]}),
|
|
136
|
+
"products": pa.table({"product_id": ["A"], "category": ["books"]}),
|
|
137
|
+
},
|
|
138
|
+
[
|
|
139
|
+
{"id": "001", "product_id": "A", "category": "books"},
|
|
140
|
+
{"id": "002", "product_id": "Z", "category": None},
|
|
141
|
+
],
|
|
142
|
+
),
|
|
143
|
+
Case(
|
|
144
|
+
contract("aggregations", {"items": grouped}, totals),
|
|
145
|
+
{
|
|
146
|
+
"items": pa.table(
|
|
147
|
+
{
|
|
148
|
+
"warehouse": ["east", "east", "east", "west"],
|
|
149
|
+
"amount": pa.array(
|
|
150
|
+
[Decimal("10.20"), None, Decimal("0.00"), Decimal("20.40")],
|
|
151
|
+
type=pa.decimal128(18, 2),
|
|
152
|
+
),
|
|
153
|
+
"id": ["001", "002", "003", "004"],
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
},
|
|
157
|
+
[
|
|
158
|
+
{"warehouse": "east", "total": Decimal("10.20"), "rows": 3},
|
|
159
|
+
{"warehouse": "west", "total": Decimal("20.40"), "rows": 1},
|
|
160
|
+
],
|
|
161
|
+
),
|
|
162
|
+
Case(
|
|
163
|
+
contract("dates", {"readings": stamps}, days),
|
|
164
|
+
{
|
|
165
|
+
"readings": pa.table(
|
|
166
|
+
{
|
|
167
|
+
"sensor_id": ["0007", "0008"],
|
|
168
|
+
"recorded_at": pa.array(
|
|
169
|
+
[
|
|
170
|
+
datetime.fromisoformat("2024-02-29T23:59:59.123456+00:00"),
|
|
171
|
+
None,
|
|
172
|
+
],
|
|
173
|
+
type=pa.timestamp("us", tz="UTC"),
|
|
174
|
+
),
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
},
|
|
178
|
+
[{"sensor_id": "0007", "day": date(2024, 2, 29)}, {"sensor_id": "0008", "day": None}],
|
|
179
|
+
),
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def pandas_transform(tables: Mapping[str, pd.DataFrame], ctx: ef.RunContext) -> pd.DataFrame:
|
|
184
|
+
family = ctx.plan.contract.family
|
|
185
|
+
if family == "joins":
|
|
186
|
+
return tables["orders"].merge(tables["products"], on="product_id", how="left", sort=False)
|
|
187
|
+
if family == "aggregations":
|
|
188
|
+
return (
|
|
189
|
+
tables["items"]
|
|
190
|
+
.groupby("warehouse", dropna=False, sort=False)
|
|
191
|
+
.agg(
|
|
192
|
+
total=("amount", "sum"),
|
|
193
|
+
rows=("id", "size"),
|
|
194
|
+
)
|
|
195
|
+
.reset_index()
|
|
196
|
+
)
|
|
197
|
+
if family == "dates":
|
|
198
|
+
import pandas as pd
|
|
199
|
+
|
|
200
|
+
frame = tables["readings"]
|
|
201
|
+
# This fixture declares UTC microseconds. Use Pandas' own temporal
|
|
202
|
+
# implementation: Arrow-backed .dt.date needs an additional C++ IANA
|
|
203
|
+
# database on some Windows wheels, independently of Python tzdata.
|
|
204
|
+
recorded_at: pd.Series[pd.Timestamp] = pd.Series(
|
|
205
|
+
frame["recorded_at"].tolist(), dtype="datetime64[us, UTC]", index=frame.index
|
|
206
|
+
)
|
|
207
|
+
day: pd.Series[date] = recorded_at.dt.date.astype(pd.ArrowDtype(pa.date32()))
|
|
208
|
+
return frame[["sensor_id"]].assign(day=day)
|
|
209
|
+
return tables["items"].copy(deep=True)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def polars_transform(tables: Mapping[str, pl.DataFrame], ctx: ef.RunContext) -> pl.LazyFrame:
|
|
213
|
+
import polars as pl
|
|
214
|
+
|
|
215
|
+
family = ctx.plan.contract.family
|
|
216
|
+
if family == "joins":
|
|
217
|
+
return (
|
|
218
|
+
tables["orders"]
|
|
219
|
+
.lazy()
|
|
220
|
+
.join(tables["products"].lazy(), on="product_id", how="left", maintain_order="left")
|
|
221
|
+
)
|
|
222
|
+
if family == "aggregations":
|
|
223
|
+
return (
|
|
224
|
+
tables["items"]
|
|
225
|
+
.lazy()
|
|
226
|
+
.group_by("warehouse", maintain_order=True)
|
|
227
|
+
.agg(
|
|
228
|
+
pl.col("amount").sum().alias("total"),
|
|
229
|
+
pl.len().alias("rows"),
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
if family == "dates":
|
|
233
|
+
return (
|
|
234
|
+
tables["readings"]
|
|
235
|
+
.lazy()
|
|
236
|
+
.select("sensor_id", pl.col("recorded_at").dt.date().alias("day"))
|
|
237
|
+
)
|
|
238
|
+
return tables["items"].lazy().select(pl.all())
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def registry() -> ef.TransformationRegistry:
|
|
242
|
+
result = ef.TransformationRegistry()
|
|
243
|
+
sql = {
|
|
244
|
+
"nulls_types": 'SELECT * FROM "items"',
|
|
245
|
+
"duplicates_order": 'SELECT * FROM "items"',
|
|
246
|
+
"joins": 'SELECT o.id, o.product_id, p.category FROM "orders" o '
|
|
247
|
+
'LEFT JOIN "products" p ON o.product_id=p.product_id ORDER BY o.id',
|
|
248
|
+
"aggregations": 'SELECT warehouse, sum(amount) AS total, count(*) AS rows FROM "items" '
|
|
249
|
+
"GROUP BY warehouse",
|
|
250
|
+
"dates": 'SELECT sensor_id, CAST(recorded_at AS DATE) AS day FROM "readings"',
|
|
251
|
+
}
|
|
252
|
+
for family, query in sql.items():
|
|
253
|
+
result.register(ef.PandasTransformation(family, pandas_transform, "synthetic.v1"))
|
|
254
|
+
result.register(ef.PolarsTransformation(family, polars_transform, "synthetic.v1"))
|
|
255
|
+
result.register(ef.DuckDBTransformation(family, query, "synthetic.v1"))
|
|
256
|
+
return result
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def assert_expected(actual: list[dict[str, object]], expected: list[dict[str, object]]) -> None:
|
|
260
|
+
if not (len(actual) == len(expected)):
|
|
261
|
+
raise AssertionError("Internal invariant check failed.")
|
|
262
|
+
for observed, wanted in zip(actual, expected, strict=True):
|
|
263
|
+
if not (observed.keys() == wanted.keys()):
|
|
264
|
+
raise AssertionError("Internal invariant check failed.")
|
|
265
|
+
for key, value in wanted.items():
|
|
266
|
+
if isinstance(value, float) and math.isnan(value):
|
|
267
|
+
actual_value = observed[key]
|
|
268
|
+
if not (isinstance(actual_value, float) and math.isnan(actual_value)):
|
|
269
|
+
raise AssertionError("Internal invariant check failed.")
|
|
270
|
+
else:
|
|
271
|
+
if not (observed[key] == value):
|
|
272
|
+
raise AssertionError("Internal invariant check failed.")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def expected_table(case: Case) -> pa.Table:
|
|
276
|
+
"""Manually specified expected physical schemas, independent of normalization."""
|
|
277
|
+
schemas = {
|
|
278
|
+
"nulls_types": pa.schema(
|
|
279
|
+
[("id", pa.string()), ("amount", pa.decimal128(18, 2)), ("measurement", pa.float64())]
|
|
280
|
+
),
|
|
281
|
+
"duplicates_order": pa.schema(
|
|
282
|
+
[("id", pa.string()), ("amount", pa.decimal128(18, 2)), ("measurement", pa.float64())]
|
|
283
|
+
),
|
|
284
|
+
"joins": pa.schema(
|
|
285
|
+
[("id", pa.string()), ("product_id", pa.string()), ("category", pa.string())]
|
|
286
|
+
),
|
|
287
|
+
"aggregations": pa.schema(
|
|
288
|
+
[("warehouse", pa.string()), ("total", pa.decimal128(38, 2)), ("rows", pa.int64())]
|
|
289
|
+
),
|
|
290
|
+
"dates": pa.schema([("sensor_id", pa.string()), ("day", pa.date32())]),
|
|
291
|
+
}
|
|
292
|
+
return pa.Table.from_pylist(case.expected, schema=schemas[case.contract.family])
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
COMPARISON_POLICY = ef.ComparisonPolicy(
|
|
296
|
+
physical_types="lossless",
|
|
297
|
+
index="ignore",
|
|
298
|
+
justification="Only declared columns carry meaning; lossless native widths allowed.",
|
|
299
|
+
)
|