lumis-ledger 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Axiom Photonics Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: lumis-ledger
3
+ Version: 0.1.0
4
+ Summary: An honest, read-only power ledger for your machine, from Axiom Photonics.
5
+ Author: Axiom Photonics Ltd
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Pr3ndy-ctrl/lumis-ledger
8
+ Project-URL: SDK, https://pypi.org/project/axiom-lumis/
9
+ Keywords: power,energy,monitor,photonics,lumis,cli
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: psutil>=5.9
14
+ Requires-Dist: rich>=13.0
15
+ Dynamic: license-file
16
+
17
+ # lumis-ledger
18
+
19
+ An honest, read-only power ledger for your machine. A small cross-platform
20
+ terminal tool from [Axiom Photonics](https://axiomprocessor.com).
21
+
22
+ It shows where your compute is going, and it is honest about what it cannot
23
+ measure. On most platforms software cannot read true wall power, so it says so
24
+ instead of inventing a number. Where the OS does expose energy (Linux RAPL) it
25
+ reports the measured figure.
26
+
27
+ It also carries the one thing a normal system monitor does not: a **LUMIS
28
+ estimator**. Give it a layer size and it prints what that linear layer would
29
+ cost on a LUMIS Rev 1 photonic node, as a labelled pre-silicon design target,
30
+ beside a rough digital figure.
31
+
32
+ ```
33
+ pip install lumis-ledger
34
+ ```
35
+
36
+ ## Use
37
+
38
+ ```
39
+ lumis-ledger # one snapshot of this machine
40
+ lumis-ledger --watch # refresh until Ctrl-C
41
+ lumis-ledger estimate --size 64 # what a 64x64 layer costs on a photonic node
42
+ lumis-ledger estimate --size 128 --rate 1e10
43
+ ```
44
+
45
+ ## What it measures, and what it does not
46
+
47
+ - **Compute load, memory, swap, load average, battery, top consumers**: read
48
+ live from the OS through `psutil`. These are real.
49
+ - **Total power in watts**: reported only where the OS exposes it. On Linux with
50
+ RAPL you get a measured figure. On macOS, Windows and most virtual machines
51
+ the wall power is not available to software, so the tool shows `—` and says
52
+ "not directly measurable" rather than guessing. That honesty is the point.
53
+ - **The platform idle floor**: not exposed by any consumer OS, so it is left
54
+ blank rather than modelled.
55
+
56
+ ## Read only
57
+
58
+ This tool changes nothing on your system, runs no privileged operations, and
59
+ installs no background service. It reads counters and prints them. That is all.
60
+
61
+ ## The LUMIS estimator
62
+
63
+ `lumis-ledger estimate` applies the same energy model as the Axiom photonic
64
+ compiler. A photonic mesh does the matrix multiply by propagating light, so the
65
+ arithmetic is effectively free; what the node draws is a fixed floor (drivers,
66
+ laser and electro-optic hold) that does not grow with the compute rate. So the
67
+ comparison against a digital machine is not a flat "we win", it is a breakeven:
68
+
69
+ ```
70
+ A 64x64 linear layer on a photonic node (LUMIS Rev 1 design targets)
71
+ MZIs in the mesh 2016
72
+ DIGITAL @ 1e9/s 18.842 W (scales with the rate)
73
+ PHOTONIC floor 7.000 W (flat vs the rate)
74
+ BREAKEVEN 3.72e8 matvec/s -> above it the photonic node wins
75
+ ```
76
+
77
+ Every hardware figure is a **design target for a chip that is not yet
78
+ fabricated**, and every one is reproducible with the free SDK:
79
+
80
+ ```
81
+ pip install axiom-lumis
82
+ ```
83
+
84
+ The SDK is the real compiler (analytic Clements decomposition, machine-precision
85
+ reconstruction, admission against the 8 W node envelope). This tool is just a
86
+ friendly window onto its energy model, on hardware you already own.
87
+
88
+ ## Licence
89
+
90
+ MIT. Free to use, fork and ship.
@@ -0,0 +1,74 @@
1
+ # lumis-ledger
2
+
3
+ An honest, read-only power ledger for your machine. A small cross-platform
4
+ terminal tool from [Axiom Photonics](https://axiomprocessor.com).
5
+
6
+ It shows where your compute is going, and it is honest about what it cannot
7
+ measure. On most platforms software cannot read true wall power, so it says so
8
+ instead of inventing a number. Where the OS does expose energy (Linux RAPL) it
9
+ reports the measured figure.
10
+
11
+ It also carries the one thing a normal system monitor does not: a **LUMIS
12
+ estimator**. Give it a layer size and it prints what that linear layer would
13
+ cost on a LUMIS Rev 1 photonic node, as a labelled pre-silicon design target,
14
+ beside a rough digital figure.
15
+
16
+ ```
17
+ pip install lumis-ledger
18
+ ```
19
+
20
+ ## Use
21
+
22
+ ```
23
+ lumis-ledger # one snapshot of this machine
24
+ lumis-ledger --watch # refresh until Ctrl-C
25
+ lumis-ledger estimate --size 64 # what a 64x64 layer costs on a photonic node
26
+ lumis-ledger estimate --size 128 --rate 1e10
27
+ ```
28
+
29
+ ## What it measures, and what it does not
30
+
31
+ - **Compute load, memory, swap, load average, battery, top consumers**: read
32
+ live from the OS through `psutil`. These are real.
33
+ - **Total power in watts**: reported only where the OS exposes it. On Linux with
34
+ RAPL you get a measured figure. On macOS, Windows and most virtual machines
35
+ the wall power is not available to software, so the tool shows `—` and says
36
+ "not directly measurable" rather than guessing. That honesty is the point.
37
+ - **The platform idle floor**: not exposed by any consumer OS, so it is left
38
+ blank rather than modelled.
39
+
40
+ ## Read only
41
+
42
+ This tool changes nothing on your system, runs no privileged operations, and
43
+ installs no background service. It reads counters and prints them. That is all.
44
+
45
+ ## The LUMIS estimator
46
+
47
+ `lumis-ledger estimate` applies the same energy model as the Axiom photonic
48
+ compiler. A photonic mesh does the matrix multiply by propagating light, so the
49
+ arithmetic is effectively free; what the node draws is a fixed floor (drivers,
50
+ laser and electro-optic hold) that does not grow with the compute rate. So the
51
+ comparison against a digital machine is not a flat "we win", it is a breakeven:
52
+
53
+ ```
54
+ A 64x64 linear layer on a photonic node (LUMIS Rev 1 design targets)
55
+ MZIs in the mesh 2016
56
+ DIGITAL @ 1e9/s 18.842 W (scales with the rate)
57
+ PHOTONIC floor 7.000 W (flat vs the rate)
58
+ BREAKEVEN 3.72e8 matvec/s -> above it the photonic node wins
59
+ ```
60
+
61
+ Every hardware figure is a **design target for a chip that is not yet
62
+ fabricated**, and every one is reproducible with the free SDK:
63
+
64
+ ```
65
+ pip install axiom-lumis
66
+ ```
67
+
68
+ The SDK is the real compiler (analytic Clements decomposition, machine-precision
69
+ reconstruction, admission against the 8 W node envelope). This tool is just a
70
+ friendly window onto its energy model, on hardware you already own.
71
+
72
+ ## Licence
73
+
74
+ MIT. Free to use, fork and ship.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: lumis-ledger
3
+ Version: 0.1.0
4
+ Summary: An honest, read-only power ledger for your machine, from Axiom Photonics.
5
+ Author: Axiom Photonics Ltd
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Pr3ndy-ctrl/lumis-ledger
8
+ Project-URL: SDK, https://pypi.org/project/axiom-lumis/
9
+ Keywords: power,energy,monitor,photonics,lumis,cli
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: psutil>=5.9
14
+ Requires-Dist: rich>=13.0
15
+ Dynamic: license-file
16
+
17
+ # lumis-ledger
18
+
19
+ An honest, read-only power ledger for your machine. A small cross-platform
20
+ terminal tool from [Axiom Photonics](https://axiomprocessor.com).
21
+
22
+ It shows where your compute is going, and it is honest about what it cannot
23
+ measure. On most platforms software cannot read true wall power, so it says so
24
+ instead of inventing a number. Where the OS does expose energy (Linux RAPL) it
25
+ reports the measured figure.
26
+
27
+ It also carries the one thing a normal system monitor does not: a **LUMIS
28
+ estimator**. Give it a layer size and it prints what that linear layer would
29
+ cost on a LUMIS Rev 1 photonic node, as a labelled pre-silicon design target,
30
+ beside a rough digital figure.
31
+
32
+ ```
33
+ pip install lumis-ledger
34
+ ```
35
+
36
+ ## Use
37
+
38
+ ```
39
+ lumis-ledger # one snapshot of this machine
40
+ lumis-ledger --watch # refresh until Ctrl-C
41
+ lumis-ledger estimate --size 64 # what a 64x64 layer costs on a photonic node
42
+ lumis-ledger estimate --size 128 --rate 1e10
43
+ ```
44
+
45
+ ## What it measures, and what it does not
46
+
47
+ - **Compute load, memory, swap, load average, battery, top consumers**: read
48
+ live from the OS through `psutil`. These are real.
49
+ - **Total power in watts**: reported only where the OS exposes it. On Linux with
50
+ RAPL you get a measured figure. On macOS, Windows and most virtual machines
51
+ the wall power is not available to software, so the tool shows `—` and says
52
+ "not directly measurable" rather than guessing. That honesty is the point.
53
+ - **The platform idle floor**: not exposed by any consumer OS, so it is left
54
+ blank rather than modelled.
55
+
56
+ ## Read only
57
+
58
+ This tool changes nothing on your system, runs no privileged operations, and
59
+ installs no background service. It reads counters and prints them. That is all.
60
+
61
+ ## The LUMIS estimator
62
+
63
+ `lumis-ledger estimate` applies the same energy model as the Axiom photonic
64
+ compiler. A photonic mesh does the matrix multiply by propagating light, so the
65
+ arithmetic is effectively free; what the node draws is a fixed floor (drivers,
66
+ laser and electro-optic hold) that does not grow with the compute rate. So the
67
+ comparison against a digital machine is not a flat "we win", it is a breakeven:
68
+
69
+ ```
70
+ A 64x64 linear layer on a photonic node (LUMIS Rev 1 design targets)
71
+ MZIs in the mesh 2016
72
+ DIGITAL @ 1e9/s 18.842 W (scales with the rate)
73
+ PHOTONIC floor 7.000 W (flat vs the rate)
74
+ BREAKEVEN 3.72e8 matvec/s -> above it the photonic node wins
75
+ ```
76
+
77
+ Every hardware figure is a **design target for a chip that is not yet
78
+ fabricated**, and every one is reproducible with the free SDK:
79
+
80
+ ```
81
+ pip install axiom-lumis
82
+ ```
83
+
84
+ The SDK is the real compiler (analytic Clements decomposition, machine-precision
85
+ reconstruction, admission against the 8 W node envelope). This tool is just a
86
+ friendly window onto its energy model, on hardware you already own.
87
+
88
+ ## Licence
89
+
90
+ MIT. Free to use, fork and ship.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ lumis_ledger.py
4
+ pyproject.toml
5
+ lumis_ledger.egg-info/PKG-INFO
6
+ lumis_ledger.egg-info/SOURCES.txt
7
+ lumis_ledger.egg-info/dependency_links.txt
8
+ lumis_ledger.egg-info/entry_points.txt
9
+ lumis_ledger.egg-info/requires.txt
10
+ lumis_ledger.egg-info/top_level.txt
11
+ tests/test_ledger.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lumis-ledger = lumis_ledger:main
@@ -0,0 +1,2 @@
1
+ psutil>=5.9
2
+ rich>=13.0
@@ -0,0 +1 @@
1
+ lumis_ledger
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env python3
2
+ """lumis-ledger: an honest, read-only power ledger for your machine.
3
+
4
+ A small cross-platform terminal tool from Axiom Photonics. It shows where your
5
+ machine's compute is going, and it is honest about what it cannot measure: on
6
+ most platforms software cannot read true wall power, so it says so rather than
7
+ inventing a number. Where the OS does expose energy (Linux RAPL), it reports the
8
+ measured figure.
9
+
10
+ It also carries the one thing a normal system monitor does not: a LUMIS
11
+ estimator. Give it a layer size and it prints what that linear layer would cost
12
+ on a LUMIS Rev 1 photonic node, as a labelled pre-silicon design target, beside
13
+ a rough digital figure. Those numbers are reproducible with the free SDK:
14
+ pip install axiom-lumis.
15
+
16
+ This tool is READ ONLY. It changes nothing on your system and needs no elevated
17
+ privileges. MIT licensed. https://github.com/Pr3ndy-ctrl/lumis-ledger
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import glob
23
+ import os
24
+ import time
25
+
26
+ import psutil
27
+
28
+ __version__ = "0.1.0"
29
+ SDK_URL = "pip install axiom-lumis"
30
+
31
+ # --- LUMIS Rev 1 design-target constants (identical to the lumis-nano ledger) ---
32
+ # EO phase shifters hold a voltage, not a current, so static hold is tiny.
33
+ # Every figure here is a design target for a chip that is not yet fabricated.
34
+ P_DRIVE_LASER = 6.8 # W, modulator drivers + laser coupling floor per node
35
+ HOLD_PER_MZI = 0.2 / 2016 # W, EO static hold per MZI (<0.2 W across a full mesh)
36
+ E_MAC_DIGITAL = 4.6e-12 # J per fp32 multiply-accumulate (45 nm, Horowitz 2014)
37
+
38
+
39
+ # ----------------------------------------------------------------- power reading
40
+ def read_rapl_power(interval: float = 0.3):
41
+ """Measured package power in watts via Linux RAPL, or None where unavailable.
42
+
43
+ RAPL exposes a monotonically increasing energy counter in microjoules; the
44
+ power is the difference over a short interval. Absent on macOS, Windows,
45
+ most VMs and this value is simply not reported there."""
46
+ paths = sorted(glob.glob("/sys/class/powercap/intel-rapl:*/energy_uj"))
47
+ paths = [p for p in paths if "intel-rapl:" in p and p.count(":") == 1]
48
+ if not paths:
49
+ return None
50
+ try:
51
+ def total_uj():
52
+ return sum(int(open(p).read()) for p in paths)
53
+ a = total_uj()
54
+ time.sleep(interval)
55
+ b = total_uj()
56
+ if b < a: # counter wrapped
57
+ return None
58
+ return (b - a) / 1e6 / interval
59
+ except (OSError, ValueError, PermissionError):
60
+ return None
61
+
62
+
63
+ def sample(interval: float = 0.6) -> dict:
64
+ """One telemetry snapshot. The interval is the window CPU load is measured
65
+ over, so the first call blocks for `interval` seconds to get a real number."""
66
+ # prime per-process cpu counters, then measure over the window
67
+ procs = list(psutil.process_iter(["name"]))
68
+ for p in procs:
69
+ try:
70
+ p.cpu_percent(None)
71
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
72
+ pass
73
+ cpu_busy = psutil.cpu_percent(interval) # blocks `interval`, returns 0..100
74
+
75
+ consumers = []
76
+ ncpu = psutil.cpu_count() or 1
77
+ for p in procs:
78
+ try:
79
+ pct = p.cpu_percent(None) / ncpu # normalise to whole-machine share
80
+ name = p.info.get("name") or "?"
81
+ if pct > 0:
82
+ consumers.append((pct, name))
83
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
84
+ pass
85
+ consumers.sort(reverse=True)
86
+
87
+ vm = psutil.virtual_memory()
88
+ sw = psutil.swap_memory()
89
+ try:
90
+ load = os.getloadavg()[0]
91
+ except (OSError, AttributeError):
92
+ load = None
93
+ try:
94
+ batt = psutil.sensors_battery()
95
+ except (AttributeError, NotImplementedError):
96
+ batt = None
97
+
98
+ watts = read_rapl_power()
99
+ return {
100
+ "cpu_busy": cpu_busy,
101
+ "ncpu": ncpu,
102
+ "consumers": consumers,
103
+ "mem_used_gb": (vm.total - vm.available) / 1e9,
104
+ "mem_total_gb": vm.total / 1e9,
105
+ "swap_gb": sw.used / 1e9,
106
+ "load": load,
107
+ "battery": batt,
108
+ "watts": watts,
109
+ }
110
+
111
+
112
+ # ----------------------------------------------------------------- LUMIS estimate
113
+ def lumis_estimate(n: int, rate_hz: float = 1e9) -> dict:
114
+ """What an n-mode linear layer costs on a LUMIS Rev 1 node (design targets)."""
115
+ macs = n * n
116
+ mzis = n * (n - 1) // 2
117
+ p_static = P_DRIVE_LASER + mzis * HOLD_PER_MZI # flat vs rate
118
+ p_digital = macs * E_MAC_DIGITAL * rate_hz # scales with rate
119
+ breakeven = p_static / (macs * E_MAC_DIGITAL) # matvec/s where they cross
120
+ return {
121
+ "n": n, "macs": macs, "mzis": mzis, "rate_hz": rate_hz,
122
+ "p_static": p_static, "p_digital": p_digital, "breakeven": breakeven,
123
+ }
124
+
125
+
126
+ # ----------------------------------------------------------------------- rendering
127
+ def _bar(frac: float, width: int = 28) -> str:
128
+ frac = max(0.0, min(1.0, frac))
129
+ fill = int(round(frac * width))
130
+ return "[#6aa9ff]" + "█" * fill + "[/]" + "[#394150]" + "─" * (width - fill) + "[/]"
131
+
132
+
133
+ def render(console, s: dict) -> None:
134
+ from rich.panel import Panel
135
+ from rich.table import Table
136
+ from rich import box
137
+
138
+ # --- power ledger ---
139
+ if s["watts"] is not None:
140
+ total = f"[bold #6aa9ff]{s['watts']:.1f}[/] W [dim](measured, RAPL)[/]"
141
+ floor = "[dim]not exposed by the OS[/]"
142
+ else:
143
+ total = "[bold]—[/] W [#d98a5b]not directly measurable in software here[/]"
144
+ floor = "[dim]platform idle draw, not exposed by the OS[/]"
145
+ busy = s["cpu_busy"] / 100.0
146
+ pwr = Table.grid(padding=(0, 1))
147
+ pwr.add_column(justify="left")
148
+ pwr.add_row("[dim]AXIOM LEDGER[/] [dim]P = floor + compute[/]")
149
+ pwr.add_row("[bold]Power ledger[/]")
150
+ pwr.add_row("")
151
+ pwr.add_row(f"TOTAL {total}")
152
+ pwr.add_row(f"COMPUTE {_bar(busy)} [bold]{s['cpu_busy']:.0f}%[/] [dim]CPU load[/]")
153
+ pwr.add_row(f"FLOOR {floor}")
154
+ console.print(Panel(pwr, box=box.ROUNDED, border_style="#394150", padding=(1, 2)))
155
+
156
+ # --- system row ---
157
+ sysg = Table.grid(expand=True)
158
+ sysg.add_column(ratio=1)
159
+ sysg.add_column(ratio=1)
160
+ b = s["battery"]
161
+ if b is not None:
162
+ plug = "AC" if b.power_plugged else "battery"
163
+ batt_s = f"[bold]{b.percent:.0f}%[/] [dim]{plug}[/]"
164
+ else:
165
+ batt_s = "[dim]no battery (desktop or server)[/]"
166
+ mem_s = f"[bold]{s['mem_used_gb']:.1f} / {s['mem_total_gb']:.1f} GB[/]"
167
+ load_s = f"swap {s['swap_gb']:.2f} GB" + (f" · load {s['load']:.1f}" if s["load"] is not None else "")
168
+ left = Table.grid(); left.add_column()
169
+ left.add_row("[dim]BATTERY[/]"); left.add_row(batt_s)
170
+ right = Table.grid(); right.add_column()
171
+ right.add_row("[dim]MEMORY[/]"); right.add_row(mem_s); right.add_row(f"[dim]{load_s}[/]")
172
+ sysg.add_row(
173
+ Panel(left, box=box.ROUNDED, border_style="#394150", padding=(0, 2)),
174
+ Panel(right, box=box.ROUNDED, border_style="#394150", padding=(0, 2)),
175
+ )
176
+ console.print(sysg)
177
+
178
+ # --- top consumers ---
179
+ tc = Table(box=box.ROUNDED, border_style="#394150", expand=True,
180
+ title="[dim]TOP CONSUMERS[/]", title_justify="left", show_header=False)
181
+ tc.add_column(justify="right", width=7)
182
+ tc.add_column()
183
+ for pct, name in s["consumers"][:8]:
184
+ colour = "#d9a441" if pct >= 20 else "white"
185
+ tc.add_row(f"[{colour}]{pct:.1f}%[/]", name)
186
+ if not s["consumers"][:8]:
187
+ tc.add_row("", "[dim]no active processes sampled[/]")
188
+ console.print(tc)
189
+
190
+ console.print(
191
+ f"[dim]read only, nothing on your system was changed. "
192
+ f"compare against a photonic node:[/] [bold]lumis-ledger estimate --size 64[/] "
193
+ f"[dim]· {SDK_URL}[/]"
194
+ )
195
+
196
+
197
+ def render_estimate(console, e: dict) -> None:
198
+ from rich.panel import Panel
199
+ from rich.table import Table
200
+ from rich import box
201
+ verdict = "photonic wins" if e["rate_hz"] > e["breakeven"] else "digital still cheaper"
202
+ g = Table.grid(padding=(0, 1))
203
+ g.add_column()
204
+ g.add_row("[dim]AXIOM LEDGER · LUMIS Rev 1 design targets[/]")
205
+ g.add_row(f"[bold]A {e['n']}x{e['n']} linear layer on a photonic node[/]")
206
+ g.add_row("")
207
+ g.add_row(f"MZIs in the mesh [bold]{e['mzis']}[/] [dim](N(N-1)/2)[/]")
208
+ g.add_row(f"DIGITAL @ {e['rate_hz']:.0e}/s [bold]{e['p_digital']:.3f} W[/] [dim]{e['macs']} MACs x 4.6 pJ x rate, scales with rate[/]")
209
+ g.add_row(f"PHOTONIC floor [bold]{e['p_static']:.3f} W[/] [dim]6.8 W drivers+laser + EO hold, flat vs rate[/]")
210
+ g.add_row(f"BREAKEVEN [bold]{e['breakeven']:.2e} matvec/s[/] [dim]at {e['rate_hz']:.0e}/s: {verdict}[/]")
211
+ g.add_row("")
212
+ g.add_row("[dim]The arithmetic is free on a photonic mesh; light does the multiply.[/]")
213
+ g.add_row("[dim]The node is not: it draws a fixed floor whatever the rate. All figures[/]")
214
+ g.add_row(f"[dim]are Rev 1 design targets for an unfabricated chip. Reproduce them: {SDK_URL}[/]")
215
+ console.print(Panel(g, box=box.ROUNDED, border_style="#394150", padding=(1, 2)))
216
+
217
+
218
+ # ---------------------------------------------------------------------------- main
219
+ def main(argv=None) -> int:
220
+ from rich.console import Console
221
+ ap = argparse.ArgumentParser(
222
+ prog="lumis-ledger",
223
+ description="An honest, read-only power ledger for your machine, from Axiom Photonics.")
224
+ sub = ap.add_subparsers(dest="cmd")
225
+ ap.add_argument("--watch", action="store_true", help="refresh continuously until Ctrl-C")
226
+ ap.add_argument("--interval", type=float, default=2.0, help="watch refresh seconds (default 2)")
227
+ ap.add_argument("--version", action="version", version=f"lumis-ledger {__version__}")
228
+
229
+ est = sub.add_parser("estimate", help="what a linear layer costs on a LUMIS photonic node")
230
+ est.add_argument("--size", type=int, default=64, help="mesh size N (2..?), default 64")
231
+ est.add_argument("--rate", type=float, default=1e9, help="matrix-vector ops per second, default 1e9")
232
+
233
+ a = ap.parse_args(argv)
234
+ console = Console()
235
+
236
+ if a.cmd == "estimate":
237
+ if a.size < 2:
238
+ ap.error("--size must be at least 2")
239
+ render_estimate(console, lumis_estimate(a.size, a.rate))
240
+ return 0
241
+
242
+ if a.watch:
243
+ try:
244
+ while True:
245
+ console.clear()
246
+ render(console, sample())
247
+ time.sleep(max(0.5, a.interval))
248
+ except KeyboardInterrupt:
249
+ return 0
250
+ else:
251
+ render(console, sample())
252
+ return 0
253
+
254
+
255
+ if __name__ == "__main__":
256
+ raise SystemExit(main())
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lumis-ledger"
7
+ version = "0.1.0"
8
+ description = "An honest, read-only power ledger for your machine, from Axiom Photonics."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Axiom Photonics Ltd" }]
13
+ keywords = ["power", "energy", "monitor", "photonics", "lumis", "cli"]
14
+ dependencies = ["psutil>=5.9", "rich>=13.0"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/Pr3ndy-ctrl/lumis-ledger"
18
+ SDK = "https://pypi.org/project/axiom-lumis/"
19
+
20
+ [project.scripts]
21
+ lumis-ledger = "lumis_ledger:main"
22
+
23
+ [tool.setuptools]
24
+ py-modules = ["lumis_ledger"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,41 @@
1
+ """Tests for lumis-ledger. Run: python -m pytest -q"""
2
+ import math
3
+ import sys, os
4
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
5
+
6
+ import lumis_ledger as ll
7
+
8
+
9
+ def test_estimate_matches_sdk_figures_at_64():
10
+ e = ll.lumis_estimate(64, 1e9)
11
+ assert e["mzis"] == 2016
12
+ assert e["macs"] == 4096
13
+ assert math.isclose(e["p_static"], 7.000, abs_tol=1e-3) # 6.8 + 2016*(0.2/2016)
14
+ assert math.isclose(e["p_digital"], 18.8416, abs_tol=1e-3) # 4096*4.6e-12*1e9
15
+ assert math.isclose(e["breakeven"], 3.7149e8, rel_tol=1e-3)
16
+
17
+
18
+ def test_estimate_small_mesh_loses():
19
+ e = ll.lumis_estimate(8, 1e9)
20
+ assert math.isclose(e["p_static"], 6.8028, abs_tol=1e-3)
21
+ assert e["breakeven"] > e["rate_hz"] # a tiny mesh does not win at 1e9
22
+
23
+
24
+ def test_estimate_constants_match_lumis_nano():
25
+ # the whole point: these must equal the SDK's honest ledger constants
26
+ assert ll.P_DRIVE_LASER == 6.8
27
+ assert math.isclose(ll.HOLD_PER_MZI, 0.2 / 2016)
28
+ assert ll.E_MAC_DIGITAL == 4.6e-12
29
+
30
+
31
+ def test_sample_returns_expected_keys():
32
+ s = ll.sample(interval=0.1)
33
+ for k in ("cpu_busy", "consumers", "mem_used_gb", "mem_total_gb", "watts"):
34
+ assert k in s
35
+ assert 0.0 <= s["cpu_busy"] <= 100.0 * (s["ncpu"] + 1)
36
+ assert s["mem_total_gb"] > 0
37
+
38
+
39
+ def test_power_reading_is_float_or_none():
40
+ w = ll.read_rapl_power(interval=0.05)
41
+ assert w is None or (isinstance(w, float) and w >= 0)