gemla 1.0.0__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.
- gemla/__init__.py +9 -0
- gemla/__version.py +1 -0
- gemla/benchmarks/__init__.py +19 -0
- gemla/benchmarks/run_all_benchmarks.py +31 -0
- gemla/benchmarks/runner.py +148 -0
- gemla/cli/__init__.py +3 -0
- gemla/cli/main.py +469 -0
- gemla/controls/__init__.py +3 -0
- gemla/controls/control_suite.py +26 -0
- gemla/controls/phase_shuffle.py +0 -0
- gemla/controls/residue_scramble.py +0 -0
- gemla/controls/wrong_sign.py +0 -0
- gemla/core/action.py +0 -0
- gemla/core/eml.py +0 -0
- gemla/core/gamma_operator.py +0 -0
- gemla/core/surrogate.py +0 -0
- gemla/data/__init__.py +11 -0
- gemla/data/cyber.py +70 -0
- gemla/data/industrial.py +69 -0
- gemla/data/market.py +71 -0
- gemla/data/synthetic.py +34 -0
- gemla/gates/__init__.py +3 -0
- gemla/gates/anchor_gate.py +0 -0
- gemla/gates/reva_v2.py +0 -0
- gemla/gates/reva_v2_sf.py +80 -0
- gemla/gates/transport_acceptance.py +0 -0
- gemla/gates/winding_gate.py +0 -0
- gemla/integrations/__init__.py +1 -0
- gemla/integrations/latent/__init__.py +9 -0
- gemla/integrations/latent/adapter.py +84 -0
- gemla/integrations/vjepa/__init__.py +11 -0
- gemla/integrations/vjepa/adapter.py +87 -0
- gemla/lifted/__init__.py +13 -0
- gemla/lifted/anchors.py +42 -0
- gemla/lifted/lifted_phase.py +43 -0
- gemla/lifted/spectral_flatness.py +25 -0
- gemla/lifted/winding.py +27 -0
- gemla/pipelines/__init__.py +11 -0
- gemla/pipelines/gemla_pipeline.py +87 -0
- gemla/pipelines/latent_pipeline.py +107 -0
- gemla/reports/__init__.py +3 -0
- gemla/reports/markdown.py +86 -0
- gemla-1.0.0.dist-info/METADATA +333 -0
- gemla-1.0.0.dist-info/RECORD +48 -0
- gemla-1.0.0.dist-info/WHEEL +5 -0
- gemla-1.0.0.dist-info/entry_points.txt +2 -0
- gemla-1.0.0.dist-info/licenses/LICENSE +0 -0
- gemla-1.0.0.dist-info/top_level.txt +1 -0
gemla/__init__.py
ADDED
gemla/__version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from gemla.benchmarks.runner import (
|
|
2
|
+
BenchmarkCase,
|
|
3
|
+
BenchmarkRecord,
|
|
4
|
+
default_benchmark_cases,
|
|
5
|
+
run_benchmark_case,
|
|
6
|
+
run_benchmark_suite,
|
|
7
|
+
summarize_benchmark_records,
|
|
8
|
+
write_benchmark_results,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BenchmarkCase",
|
|
13
|
+
"BenchmarkRecord",
|
|
14
|
+
"default_benchmark_cases",
|
|
15
|
+
"run_benchmark_case",
|
|
16
|
+
"run_benchmark_suite",
|
|
17
|
+
"summarize_benchmark_records",
|
|
18
|
+
"write_benchmark_results",
|
|
19
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from gemla.benchmarks import (
|
|
2
|
+
run_benchmark_suite,
|
|
3
|
+
summarize_benchmark_records,
|
|
4
|
+
write_benchmark_results,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
records = run_benchmark_suite()
|
|
10
|
+
summary = summarize_benchmark_records(records)
|
|
11
|
+
paths = write_benchmark_results(records)
|
|
12
|
+
|
|
13
|
+
print("GEMLA Benchmark Suite")
|
|
14
|
+
print("---------------------")
|
|
15
|
+
print(f"Total cases: {summary['total_cases']}")
|
|
16
|
+
print(f"Passed cases: {summary['passed_cases']}")
|
|
17
|
+
print(f"Failed cases: {summary['failed_cases']}")
|
|
18
|
+
print(f"Pass rate: {summary['pass_rate']:.2%}")
|
|
19
|
+
print(f"All wrong-sign rejected: {summary['all_wrong_sign_rejected']}")
|
|
20
|
+
print(f"All phase-shuffle rejected: {summary['all_phase_shuffle_rejected']}")
|
|
21
|
+
print(f"All residue-scramble rejected: {summary['all_residue_scramble_rejected']}")
|
|
22
|
+
print(f"Minimum anchor count: {summary['min_anchor_count']}")
|
|
23
|
+
print(f"Minimum winding jumps: {summary['min_winding_jumps']}")
|
|
24
|
+
print()
|
|
25
|
+
print(f"CSV written to: {paths['csv']}")
|
|
26
|
+
print(f"JSON written to: {paths['json']}")
|
|
27
|
+
print(f"Summary written to: {paths['summary']}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from gemla.data import make_synthetic_transport
|
|
9
|
+
from gemla.pipelines import GemlaPipeline
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class BenchmarkCase:
|
|
14
|
+
name: str
|
|
15
|
+
n: int = 1200
|
|
16
|
+
t_max: float = 80.0
|
|
17
|
+
noise: float = 0.01
|
|
18
|
+
seed: int = 7
|
|
19
|
+
pipeline_seed: int = 11
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class BenchmarkRecord:
|
|
24
|
+
name: str
|
|
25
|
+
verdict: bool
|
|
26
|
+
spectral_flatness_score: float
|
|
27
|
+
wrong_sign_rejected: bool
|
|
28
|
+
phase_shuffle_rejected: bool
|
|
29
|
+
residue_scramble_rejected: bool
|
|
30
|
+
anchor_count: int
|
|
31
|
+
anchor_spacing_cv: float
|
|
32
|
+
winding_jumps: int
|
|
33
|
+
winding_cells: int
|
|
34
|
+
n: int
|
|
35
|
+
t_max: float
|
|
36
|
+
noise: float
|
|
37
|
+
seed: int
|
|
38
|
+
pipeline_seed: int
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def default_benchmark_cases() -> list[BenchmarkCase]:
|
|
42
|
+
"""
|
|
43
|
+
Minimal v0.2 benchmark suite.
|
|
44
|
+
|
|
45
|
+
These cases test whether the vertical slice remains stable across
|
|
46
|
+
modest changes in seed, noise, and trajectory length.
|
|
47
|
+
"""
|
|
48
|
+
return [
|
|
49
|
+
BenchmarkCase(name="baseline", n=1200, t_max=80.0, noise=0.01, seed=7),
|
|
50
|
+
BenchmarkCase(name="low_noise", n=1200, t_max=80.0, noise=0.005, seed=7),
|
|
51
|
+
BenchmarkCase(name="no_noise", n=1200, t_max=80.0, noise=0.0, seed=7),
|
|
52
|
+
BenchmarkCase(name="alt_seed_1", n=1200, t_max=80.0, noise=0.01, seed=13),
|
|
53
|
+
BenchmarkCase(name="alt_seed_2", n=1200, t_max=80.0, noise=0.01, seed=29),
|
|
54
|
+
BenchmarkCase(name="shorter_series", n=800, t_max=60.0, noise=0.01, seed=7),
|
|
55
|
+
BenchmarkCase(name="longer_series", n=1600, t_max=100.0, noise=0.01, seed=7),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_benchmark_case(case: BenchmarkCase) -> BenchmarkRecord:
|
|
60
|
+
X = make_synthetic_transport(
|
|
61
|
+
n=case.n,
|
|
62
|
+
t_max=case.t_max,
|
|
63
|
+
noise=case.noise,
|
|
64
|
+
seed=case.seed,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
result = GemlaPipeline(seed=case.pipeline_seed).fit_evaluate(X)
|
|
68
|
+
|
|
69
|
+
main = result.gate_result["main"]
|
|
70
|
+
controls = result.gate_result["controls"]
|
|
71
|
+
|
|
72
|
+
return BenchmarkRecord(
|
|
73
|
+
name=case.name,
|
|
74
|
+
verdict=result.verdict,
|
|
75
|
+
spectral_flatness_score=float(main["flatness"]["score"]),
|
|
76
|
+
wrong_sign_rejected=bool(controls["wrong_sign"]["rejected"]),
|
|
77
|
+
phase_shuffle_rejected=bool(controls["phase_shuffle"]["rejected"]),
|
|
78
|
+
residue_scramble_rejected=bool(controls["residue_scramble"]["rejected"]),
|
|
79
|
+
anchor_count=int(result.anchor_result["anchor_count"]),
|
|
80
|
+
anchor_spacing_cv=float(result.anchor_result["spacing_cv"]),
|
|
81
|
+
winding_jumps=int(result.winding_result["jump_count"]),
|
|
82
|
+
winding_cells=int(result.winding_result["total_winding_cells"]),
|
|
83
|
+
n=case.n,
|
|
84
|
+
t_max=case.t_max,
|
|
85
|
+
noise=case.noise,
|
|
86
|
+
seed=case.seed,
|
|
87
|
+
pipeline_seed=case.pipeline_seed,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def run_benchmark_suite(
|
|
92
|
+
cases: list[BenchmarkCase] | None = None,
|
|
93
|
+
) -> list[BenchmarkRecord]:
|
|
94
|
+
if cases is None:
|
|
95
|
+
cases = default_benchmark_cases()
|
|
96
|
+
|
|
97
|
+
return [run_benchmark_case(case) for case in cases]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def summarize_benchmark_records(records: list[BenchmarkRecord]) -> dict:
|
|
101
|
+
total = len(records)
|
|
102
|
+
passed = sum(record.verdict for record in records)
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
"total_cases": total,
|
|
106
|
+
"passed_cases": passed,
|
|
107
|
+
"failed_cases": total - passed,
|
|
108
|
+
"pass_rate": passed / total if total else 0.0,
|
|
109
|
+
"all_passed": passed == total,
|
|
110
|
+
"all_wrong_sign_rejected": all(r.wrong_sign_rejected for r in records),
|
|
111
|
+
"all_phase_shuffle_rejected": all(r.phase_shuffle_rejected for r in records),
|
|
112
|
+
"all_residue_scramble_rejected": all(r.residue_scramble_rejected for r in records),
|
|
113
|
+
"min_anchor_count": min((r.anchor_count for r in records), default=0),
|
|
114
|
+
"min_winding_jumps": min((r.winding_jumps for r in records), default=0),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def write_benchmark_results(
|
|
119
|
+
records: list[BenchmarkRecord],
|
|
120
|
+
output_dir: str | Path = "benchmarks/results",
|
|
121
|
+
) -> dict[str, Path]:
|
|
122
|
+
output_dir = Path(output_dir)
|
|
123
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
|
|
125
|
+
csv_path = output_dir / "benchmark_results.csv"
|
|
126
|
+
json_path = output_dir / "benchmark_results.json"
|
|
127
|
+
summary_path = output_dir / "benchmark_summary.json"
|
|
128
|
+
|
|
129
|
+
rows = [asdict(record) for record in records]
|
|
130
|
+
|
|
131
|
+
if rows:
|
|
132
|
+
with csv_path.open("w", newline="", encoding="utf-8") as f:
|
|
133
|
+
writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
|
|
134
|
+
writer.writeheader()
|
|
135
|
+
writer.writerows(rows)
|
|
136
|
+
else:
|
|
137
|
+
csv_path.write_text("", encoding="utf-8")
|
|
138
|
+
|
|
139
|
+
json_path.write_text(json.dumps(rows, indent=2), encoding="utf-8")
|
|
140
|
+
|
|
141
|
+
summary = summarize_benchmark_records(records)
|
|
142
|
+
summary_path.write_text(json.dumps(summary, indent=2), encoding="utf-8")
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
"csv": csv_path,
|
|
146
|
+
"json": json_path,
|
|
147
|
+
"summary": summary_path,
|
|
148
|
+
}
|
gemla/cli/__init__.py
ADDED
gemla/cli/main.py
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from gemla.benchmarks import (
|
|
8
|
+
run_benchmark_suite,
|
|
9
|
+
summarize_benchmark_records,
|
|
10
|
+
write_benchmark_results,
|
|
11
|
+
)
|
|
12
|
+
from gemla.data import make_synthetic_transport
|
|
13
|
+
from gemla.pipelines import GemlaPipeline
|
|
14
|
+
from gemla.reports import export_markdown_report
|
|
15
|
+
from gemla.integrations.latent import make_synthetic_latents
|
|
16
|
+
from gemla.pipelines import GemlaLatentPipeline
|
|
17
|
+
from gemla.integrations.vjepa import (
|
|
18
|
+
load_vjepa_embeddings,
|
|
19
|
+
save_sample_vjepa_like_embeddings,
|
|
20
|
+
)
|
|
21
|
+
from gemla.data import (
|
|
22
|
+
make_market_microstructure,
|
|
23
|
+
make_industrial_telemetry,
|
|
24
|
+
make_cyber_event_transport,
|
|
25
|
+
make_synthetic_transport
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
31
|
+
parser = argparse.ArgumentParser(
|
|
32
|
+
prog="gemla",
|
|
33
|
+
description="GEMLA: Γ–EML–α Transport Architecture CLI",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
37
|
+
|
|
38
|
+
evaluate = subparsers.add_parser(
|
|
39
|
+
"evaluate",
|
|
40
|
+
help="Run the minimal GEMLA transport evaluation pipeline.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
evaluate.add_argument(
|
|
44
|
+
"--output",
|
|
45
|
+
type=str,
|
|
46
|
+
default="reports/gemla_transport_report.md",
|
|
47
|
+
help="Path where the Markdown report will be written.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
evaluate.add_argument(
|
|
51
|
+
"--n",
|
|
52
|
+
type=int,
|
|
53
|
+
default=1200,
|
|
54
|
+
help="Number of synthetic samples.",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
evaluate.add_argument(
|
|
58
|
+
"--t-max",
|
|
59
|
+
type=float,
|
|
60
|
+
default=80.0,
|
|
61
|
+
help="Maximum synthetic time value.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
evaluate.add_argument(
|
|
65
|
+
"--noise",
|
|
66
|
+
type=float,
|
|
67
|
+
default=0.01,
|
|
68
|
+
help="Synthetic noise level.",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
evaluate.add_argument(
|
|
72
|
+
"--seed",
|
|
73
|
+
type=int,
|
|
74
|
+
default=7,
|
|
75
|
+
help="Synthetic data random seed.",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
evaluate.add_argument(
|
|
79
|
+
"--pipeline-seed",
|
|
80
|
+
type=int,
|
|
81
|
+
default=11,
|
|
82
|
+
help="Pipeline/control random seed.",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
benchmark = subparsers.add_parser(
|
|
86
|
+
"benchmark",
|
|
87
|
+
help="Run the GEMLA benchmark suite.",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
benchmark.add_argument(
|
|
91
|
+
"--output-dir",
|
|
92
|
+
type=str,
|
|
93
|
+
default="benchmarks/results",
|
|
94
|
+
help="Directory where benchmark CSV/JSON outputs will be written.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
evaluate_latent = subparsers.add_parser(
|
|
98
|
+
"evaluate-latent",
|
|
99
|
+
help="Run GEMLA transport evaluation on latent embeddings.",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
evaluate_latent.add_argument(
|
|
103
|
+
"--input",
|
|
104
|
+
type=str,
|
|
105
|
+
default=None,
|
|
106
|
+
help="Optional .npy file containing latent embeddings of shape (n_steps, latent_dim).",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
evaluate_latent.add_argument(
|
|
110
|
+
"--n",
|
|
111
|
+
type=int,
|
|
112
|
+
default=1200,
|
|
113
|
+
help="Number of synthetic latent samples if --input is not provided.",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
evaluate_latent.add_argument(
|
|
117
|
+
"--latent-dim",
|
|
118
|
+
type=int,
|
|
119
|
+
default=16,
|
|
120
|
+
help="Synthetic latent dimension if --input is not provided.",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
evaluate_latent.add_argument(
|
|
124
|
+
"--t-max",
|
|
125
|
+
type=float,
|
|
126
|
+
default=80.0,
|
|
127
|
+
help="Maximum synthetic time value.",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
evaluate_latent.add_argument(
|
|
131
|
+
"--noise",
|
|
132
|
+
type=float,
|
|
133
|
+
default=0.01,
|
|
134
|
+
help="Synthetic latent noise level.",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
evaluate_latent.add_argument(
|
|
138
|
+
"--seed",
|
|
139
|
+
type=int,
|
|
140
|
+
default=17,
|
|
141
|
+
help="Synthetic latent random seed.",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
evaluate_latent.add_argument(
|
|
145
|
+
"--component-a",
|
|
146
|
+
type=int,
|
|
147
|
+
default=0,
|
|
148
|
+
help="Latent component used as real part.",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
evaluate_latent.add_argument(
|
|
152
|
+
"--component-b",
|
|
153
|
+
type=int,
|
|
154
|
+
default=1,
|
|
155
|
+
help="Latent component used as imaginary part.",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
evaluate_vjepa = subparsers.add_parser(
|
|
159
|
+
"evaluate-vjepa",
|
|
160
|
+
help="Run GEMLA transport evaluation on V-JEPA-style embeddings.",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
evaluate_vjepa.add_argument(
|
|
164
|
+
"--input",
|
|
165
|
+
type=str,
|
|
166
|
+
default=None,
|
|
167
|
+
help="Path to a .npy file containing embeddings of shape (n_steps, latent_dim).",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
evaluate_vjepa.add_argument(
|
|
171
|
+
"--synthetic",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Generate synthetic V-JEPA-like embeddings for a demo run.",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
evaluate_vjepa.add_argument(
|
|
177
|
+
"--synthetic-output",
|
|
178
|
+
type=str,
|
|
179
|
+
default="examples/vjepa_latent_transport/sample_vjepa_like_embeddings.npy",
|
|
180
|
+
help="Where synthetic V-JEPA-like embeddings will be saved.",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
evaluate_vjepa.add_argument(
|
|
184
|
+
"--n",
|
|
185
|
+
type=int,
|
|
186
|
+
default=1200,
|
|
187
|
+
help="Number of synthetic embedding samples.",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
evaluate_vjepa.add_argument(
|
|
191
|
+
"--latent-dim",
|
|
192
|
+
type=int,
|
|
193
|
+
default=32,
|
|
194
|
+
help="Synthetic latent dimension.",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
evaluate_vjepa.add_argument(
|
|
198
|
+
"--t-max",
|
|
199
|
+
type=float,
|
|
200
|
+
default=80.0,
|
|
201
|
+
help="Maximum synthetic time value.",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
evaluate_vjepa.add_argument(
|
|
205
|
+
"--noise",
|
|
206
|
+
type=float,
|
|
207
|
+
default=0.01,
|
|
208
|
+
help="Synthetic embedding noise level.",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
evaluate_vjepa.add_argument(
|
|
212
|
+
"--seed",
|
|
213
|
+
type=int,
|
|
214
|
+
default=23,
|
|
215
|
+
help="Synthetic embedding random seed.",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
evaluate_vjepa.add_argument(
|
|
219
|
+
"--component-a",
|
|
220
|
+
type=int,
|
|
221
|
+
default=0,
|
|
222
|
+
help="Embedding component used as real part.",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
evaluate_vjepa.add_argument(
|
|
226
|
+
"--component-b",
|
|
227
|
+
type=int,
|
|
228
|
+
default=1,
|
|
229
|
+
help="Embedding component used as imaginary part.",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
demo_industrial = subparsers.add_parser(
|
|
233
|
+
"demo-industrial",
|
|
234
|
+
help="Run the GEMLA industrial telemetry demo.",
|
|
235
|
+
)
|
|
236
|
+
demo_industrial.add_argument(
|
|
237
|
+
"--output",
|
|
238
|
+
type=str,
|
|
239
|
+
default="reports/industrial_telemetry_report.md",
|
|
240
|
+
help="Path where the Markdown report will be written.",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
demo_market = subparsers.add_parser(
|
|
244
|
+
"demo-market",
|
|
245
|
+
help="Run the GEMLA market microstructure demo.",
|
|
246
|
+
)
|
|
247
|
+
demo_market.add_argument(
|
|
248
|
+
"--output",
|
|
249
|
+
type=str,
|
|
250
|
+
default="reports/market_microstructure_report.md",
|
|
251
|
+
help="Path where the Markdown report will be written.",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
demo_cyber = subparsers.add_parser(
|
|
255
|
+
"demo-cyber",
|
|
256
|
+
help="Run the GEMLA cyber event transport demo.",
|
|
257
|
+
)
|
|
258
|
+
demo_cyber.add_argument(
|
|
259
|
+
"--output",
|
|
260
|
+
type=str,
|
|
261
|
+
default="reports/cyber_event_transport_report.md",
|
|
262
|
+
help="Path where the Markdown report will be written.",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return parser
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def run_evaluate(args: argparse.Namespace) -> int:
|
|
269
|
+
X = make_synthetic_transport(
|
|
270
|
+
n=args.n,
|
|
271
|
+
t_max=args.t_max,
|
|
272
|
+
noise=args.noise,
|
|
273
|
+
seed=args.seed,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
pipe = GemlaPipeline(seed=args.pipeline_seed)
|
|
277
|
+
result = pipe.fit_evaluate(X)
|
|
278
|
+
|
|
279
|
+
print(result.summary())
|
|
280
|
+
|
|
281
|
+
report_path = export_markdown_report(
|
|
282
|
+
result,
|
|
283
|
+
output_path=Path(args.output),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
print()
|
|
287
|
+
print(f"Report written to: {report_path}")
|
|
288
|
+
|
|
289
|
+
return 0 if result.verdict else 1
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def run_benchmark(args: argparse.Namespace) -> int:
|
|
293
|
+
records = run_benchmark_suite()
|
|
294
|
+
summary = summarize_benchmark_records(records)
|
|
295
|
+
paths = write_benchmark_results(
|
|
296
|
+
records,
|
|
297
|
+
output_dir=Path(args.output_dir),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
print("GEMLA Benchmark Suite")
|
|
301
|
+
print("---------------------")
|
|
302
|
+
print(f"Total cases: {summary['total_cases']}")
|
|
303
|
+
print(f"Passed cases: {summary['passed_cases']}")
|
|
304
|
+
print(f"Failed cases: {summary['failed_cases']}")
|
|
305
|
+
print(f"Pass rate: {summary['pass_rate']:.2%}")
|
|
306
|
+
print(f"All wrong-sign rejected: {summary['all_wrong_sign_rejected']}")
|
|
307
|
+
print(f"All phase-shuffle rejected: {summary['all_phase_shuffle_rejected']}")
|
|
308
|
+
print(f"All residue-scramble rejected: {summary['all_residue_scramble_rejected']}")
|
|
309
|
+
print(f"Minimum anchor count: {summary['min_anchor_count']}")
|
|
310
|
+
print(f"Minimum winding jumps: {summary['min_winding_jumps']}")
|
|
311
|
+
print()
|
|
312
|
+
print(f"CSV written to: {paths['csv']}")
|
|
313
|
+
print(f"JSON written to: {paths['json']}")
|
|
314
|
+
print(f"Summary written to: {paths['summary']}")
|
|
315
|
+
|
|
316
|
+
return 0 if summary["all_passed"] else 1
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def main() -> int:
|
|
320
|
+
parser = build_parser()
|
|
321
|
+
args = parser.parse_args()
|
|
322
|
+
|
|
323
|
+
if args.command == "evaluate":
|
|
324
|
+
return run_evaluate(args)
|
|
325
|
+
|
|
326
|
+
if args.command == "benchmark":
|
|
327
|
+
return run_benchmark(args)
|
|
328
|
+
|
|
329
|
+
if args.command == "evaluate-latent":
|
|
330
|
+
return run_evaluate_latent(args)
|
|
331
|
+
|
|
332
|
+
if args.command == "evaluate-vjepa":
|
|
333
|
+
return run_evaluate_vjepa(args)
|
|
334
|
+
|
|
335
|
+
if args.command == "demo-industrial":
|
|
336
|
+
return run_demo_industrial(args)
|
|
337
|
+
|
|
338
|
+
if args.command == "demo-market":
|
|
339
|
+
return run_demo_market(args)
|
|
340
|
+
|
|
341
|
+
if args.command == "demo-cyber":
|
|
342
|
+
return run_demo_cyber(args)
|
|
343
|
+
|
|
344
|
+
parser.print_help()
|
|
345
|
+
return 2
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
if __name__ == "__main__":
|
|
349
|
+
raise SystemExit(main())
|
|
350
|
+
|
|
351
|
+
def run_evaluate_latent(args: argparse.Namespace) -> int:
|
|
352
|
+
if args.input is not None:
|
|
353
|
+
latents = np.load(args.input)
|
|
354
|
+
else:
|
|
355
|
+
latents = make_synthetic_latents(
|
|
356
|
+
n=args.n,
|
|
357
|
+
latent_dim=args.latent_dim,
|
|
358
|
+
t_max=args.t_max,
|
|
359
|
+
noise=args.noise,
|
|
360
|
+
seed=args.seed,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
pipe = GemlaLatentPipeline(
|
|
364
|
+
component_a=args.component_a,
|
|
365
|
+
component_b=args.component_b,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
result = pipe.fit_evaluate(latents)
|
|
369
|
+
|
|
370
|
+
print(result.summary())
|
|
371
|
+
|
|
372
|
+
return 0 if result.verdict else 1
|
|
373
|
+
def run_evaluate_vjepa(args: argparse.Namespace) -> int:
|
|
374
|
+
if args.synthetic:
|
|
375
|
+
embedding_path = save_sample_vjepa_like_embeddings(
|
|
376
|
+
args.synthetic_output,
|
|
377
|
+
n=args.n,
|
|
378
|
+
latent_dim=args.latent_dim,
|
|
379
|
+
t_max=args.t_max,
|
|
380
|
+
noise=args.noise,
|
|
381
|
+
seed=args.seed,
|
|
382
|
+
)
|
|
383
|
+
embeddings = load_vjepa_embeddings(embedding_path)
|
|
384
|
+
elif args.input is not None:
|
|
385
|
+
embedding_path = Path(args.input)
|
|
386
|
+
embeddings = load_vjepa_embeddings(embedding_path)
|
|
387
|
+
else:
|
|
388
|
+
print("Error: provide --input path/to/embeddings.npy or use --synthetic.")
|
|
389
|
+
return 2
|
|
390
|
+
|
|
391
|
+
pipe = GemlaLatentPipeline(
|
|
392
|
+
component_a=args.component_a,
|
|
393
|
+
component_b=args.component_b,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
result = pipe.fit_evaluate(embeddings)
|
|
397
|
+
|
|
398
|
+
print("GEMLA V-JEPA-Style Latent Transport Evaluation")
|
|
399
|
+
print("----------------------------------------------")
|
|
400
|
+
print(f"Input embeddings: {embedding_path}")
|
|
401
|
+
print()
|
|
402
|
+
print(result.summary())
|
|
403
|
+
|
|
404
|
+
return 0 if result.verdict else 1
|
|
405
|
+
|
|
406
|
+
def run_demo_industrial(args: argparse.Namespace) -> int:
|
|
407
|
+
X = make_industrial_telemetry()
|
|
408
|
+
|
|
409
|
+
pipe = GemlaPipeline()
|
|
410
|
+
result = pipe.fit_evaluate(X)
|
|
411
|
+
|
|
412
|
+
print("GEMLA Industrial Telemetry Demo")
|
|
413
|
+
print("-------------------------------")
|
|
414
|
+
print(result.summary())
|
|
415
|
+
|
|
416
|
+
report_path = export_markdown_report(
|
|
417
|
+
result,
|
|
418
|
+
output_path=Path(args.output),
|
|
419
|
+
title="GEMLA Industrial Telemetry Report",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
print()
|
|
423
|
+
print(f"Report written to: {report_path}")
|
|
424
|
+
|
|
425
|
+
return 0 if result.verdict else 1
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def run_demo_market(args: argparse.Namespace) -> int:
|
|
429
|
+
X = make_market_microstructure()
|
|
430
|
+
|
|
431
|
+
pipe = GemlaPipeline()
|
|
432
|
+
result = pipe.fit_evaluate(X)
|
|
433
|
+
|
|
434
|
+
print("GEMLA Market Microstructure Demo")
|
|
435
|
+
print("--------------------------------")
|
|
436
|
+
print(result.summary())
|
|
437
|
+
|
|
438
|
+
report_path = export_markdown_report(
|
|
439
|
+
result,
|
|
440
|
+
output_path=Path(args.output),
|
|
441
|
+
title="GEMLA Market Microstructure Report",
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
print()
|
|
445
|
+
print(f"Report written to: {report_path}")
|
|
446
|
+
|
|
447
|
+
return 0 if result.verdict else 1
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def run_demo_cyber(args: argparse.Namespace) -> int:
|
|
451
|
+
X = make_cyber_event_transport()
|
|
452
|
+
|
|
453
|
+
pipe = GemlaPipeline()
|
|
454
|
+
result = pipe.fit_evaluate(X)
|
|
455
|
+
|
|
456
|
+
print("GEMLA Cyber Event Transport Demo")
|
|
457
|
+
print("--------------------------------")
|
|
458
|
+
print(result.summary())
|
|
459
|
+
|
|
460
|
+
report_path = export_markdown_report(
|
|
461
|
+
result,
|
|
462
|
+
output_path=Path(args.output),
|
|
463
|
+
title="GEMLA Cyber Event Transport Report",
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
print()
|
|
467
|
+
print(f"Report written to: {report_path}")
|
|
468
|
+
|
|
469
|
+
return 0 if result.verdict else 1
|