Skip to content

Commit 343e9ea

Browse files
committed
feat: update version to 1.4.1 and enhance functionality with SigmaGuard integration, improved compatibility checks, and expanded documentation
1 parent 55be717 commit 343e9ea

File tree

16 files changed

+343
-65
lines changed

16 files changed

+343
-65
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ FULL.md
1515
dist/
1616
build/
1717
docs/
18+
.github/

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 1.4.1 — 2026-03-13
4+
5+
- Hardened antenna and receiver compatibility checks: pole-count validation, loka/polarity metadata matching, and keyed frequency gating now live directly in the device pipeline.
6+
- Integrated `SigmaGuard` into receiver-side purification flows and aligned `secure_transmission` / `property_transfer_chain` with the explicit O2 cleanup path.
7+
- Expanded theory-facing coverage for odd-N cascades, translation-gap branches, `probability_tensor()`, and clean-state witness/noise validation.
8+
- Reworded tetradic/pseudo-quantum helper metadata so complex arithmetic, phase operators, and direct composition are clearly marked as projection/reference vocabulary rather than primary multipolar law.
9+
310
## 1.4.0 — 2026-02-05
411

512
- Added weighted linear Σ (Σ_c = ∑(cᵢ·aᵢ)) support via `linear_coeffs` in `physics/sigma.py` and `physics/pseudomultipolar_timeseries.py`.

applications/scenarios.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,16 @@ def object_polarity_scan(params: Dict[str, Any]) -> None:
5858
def secure_transmission(params: Dict[str, Any]) -> None:
5959
key = DynamicKey(polarities=[3, 4, 5, 6], freqs_hz=[110.0, 130.0, 150.0, 170.0])
6060
tx = MultipolarTransmitter(MultipolarOscillator([_default_inductor()], [_default_capacitor()]), key=key)
61+
guard = SigmaGuard(sections=2)
6162
rx = MultipolarReceiver(
6263
MultipolarOscillator([_default_inductor()], [_default_capacitor()]),
6364
key=DynamicKey(polarities=[3, 4, 5, 6], freqs_hz=[110.0, 130.0, 150.0, 170.0]),
65+
sigma_guard=guard,
6466
)
6567
tx_antennas: Dict[int, MultipolarAntenna] = {}
6668
rx_antennas: Dict[int, MultipolarAntenna] = {}
6769
messages = list(params.get("messages", [1, 0, 2, 3, 1, 3]))
6870
good: List[int] = []
69-
guard = SigmaGuard(sections=2)
7071
for msg in messages:
7172
wave = tx.transmit([msg])
7273
n = wave.n_conjugates
@@ -75,24 +76,7 @@ def secure_transmission(params: Dict[str, Any]) -> None:
7576
emitted = tx_ant.emit(wave)
7677
received = rx_ant.receive(emitted)
7778
if rx.receive(received):
78-
# Apply SigmaGuard (NX) before decoding to enforce Σ→0
79-
mv = received.to_multipolar_value(rx.loka)
80-
purified = guard.apply_nx(mv, sections=guard.sections)[-1]
81-
purified_vec = np.asarray(
82-
[purified.coefficients.get(p, 0.0) for p in purified.loka.polarities],
83-
dtype=np.complex128,
84-
)
85-
purified_wave = MultiConjugateFunction(
86-
purified,
87-
n_conjugates=received.n_conjugates,
88-
metadata=WaveMetadata.from_amplitudes(
89-
purified_vec,
90-
loka_name=purified.loka.name,
91-
polarity_names=[p.name for p in purified.loka.polarities],
92-
frequency_hz=(received.metadata.frequency_hz if received.metadata else None),
93-
),
94-
)
95-
good.extend(rx.demodulate(purified_wave))
79+
good.extend(rx.demodulate())
9680
outdir = _outdir(params, "runs/secure_transmission")
9781
(outdir / "result.json").write_text(json.dumps({"sent": messages, "received": good}, indent=2))
9882

@@ -123,12 +107,16 @@ def property_transfer_chain(params: Dict[str, Any]) -> None:
123107
tx_osc = MultipolarOscillator([_default_inductor()], [_default_capacitor()], mind=mind)
124108
rx_osc = MultipolarOscillator([_default_inductor()], [_default_capacitor()], mind=mind)
125109
tx = MultipolarTransmitter(tx_osc, mind=mind)
126-
rx = MultipolarReceiver(rx_osc, mind=mind)
110+
rx = MultipolarReceiver(rx_osc, mind=mind, sigma_guard=SigmaGuard())
111+
tx_ant = MultipolarAntenna(polarity=tx.rank, role="tx")
112+
rx_ant = MultipolarAntenna(polarity=rx.rank, role="rx")
127113
decoded: List[int] = []
128114
for msg in messages:
129115
wave = tx.transmit([msg])
130-
rx.receive(wave)
131-
decoded.extend(rx.demodulate())
116+
emitted = tx_ant.emit(wave)
117+
received = rx_ant.receive(emitted)
118+
if rx.receive(received):
119+
decoded.extend(rx.demodulate())
132120
outdir = _outdir(params, "runs/property_transfer")
133121
(outdir / "messages.json").write_text(json.dumps({"sent": messages, "decoded": decoded}, indent=2))
134122

@@ -359,6 +347,15 @@ def pseudo_quantum_witness_pack(params: Dict[str, Any]) -> None:
359347
else:
360348
psi = psi / norm
361349
sigma_in = complex(psi.sum())
350+
psi = psi - (sigma_in / float(dim))
351+
clean_norm = float(np.linalg.norm(psi))
352+
if clean_norm == 0.0:
353+
psi = (np.ones(dim, dtype=np.complex128) + 0.0j) / np.sqrt(dim)
354+
psi = psi - complex(psi.sum()) / float(dim)
355+
clean_norm = float(np.linalg.norm(psi))
356+
psi = psi / clean_norm
357+
sigma_after_clean = complex(psi.sum())
358+
clean_state = bool(abs(sigma_after_clean) < 1e-10)
362359

363360
rng_consistent = np.random.default_rng(None if seed is None else seed + 1)
364361
rng_generic = np.random.default_rng(None if seed is None else seed + 2)
@@ -380,10 +377,12 @@ def pseudo_quantum_witness_pack(params: Dict[str, Any]) -> None:
380377
"sigma_noise": {
381378
"dim": dim,
382379
"sigma_in": [float(sigma_in.real), float(sigma_in.imag)],
380+
"sigma_after_clean": [float(sigma_after_clean.real), float(sigma_after_clean.imag)],
381+
"clean_state": clean_state,
383382
"sigma_out_sigma_consistent": [float(sigma_out_sigma.real), float(sigma_out_sigma.imag)],
384383
"sigma_out_generic": [float(sigma_out_generic.real), float(sigma_out_generic.imag)],
385-
"delta_sigma_consistent": float(abs(sigma_out_sigma - sigma_in)),
386-
"delta_sigma_generic": float(abs(sigma_out_generic - sigma_in)),
384+
"delta_sigma_consistent": float(abs(sigma_out_sigma - sigma_after_clean)),
385+
"delta_sigma_generic": float(abs(sigma_out_generic - sigma_after_clean)),
387386
},
388387
},
389388
indent=2,

core/algebras.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,11 @@ def __init__(self, n: int, operation_type: str, loka_name: str, polarity_names:
583583
1: "unit_conserve",
584584
2: "binary_linear",
585585
3: "triadic_superposition",
586-
4: "complex_wave",
586+
4: "tetradic_projection",
587587
5: "quaternion_harloka",
588588
6: "relational_visibility",
589589
7: "relational_visibility",
590-
8: "complex_wave",
590+
8: "tetradic_projection",
591591
9: "quaternion_harloka",
592592
10: "relational_visibility",
593593
}

core/loka.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ class TattvaProfile:
6363
intensity_mode="sigma_mul",
6464
),
6565
DharmaProfile(
66-
name="complex_wave_kernel",
67-
relations=("exp(i*phi)", "r^2 = ln(1 + w/eps)"),
66+
name="tetradic_projection_kernel",
67+
relations=("phase_projection(phi)", "amplitude_response(w, eps)"),
6868
polarity_arity=4,
6969
theory_refs=("complex_wave_behaviour",),
70-
description="Four-pole complex waves mix amplitude and phase in the Sigma ladder.",
71-
intensity_mode="sigma_phase",
70+
description="Four-pole tetradic projections mix amplitude-like and phase-like coordinates in Sigma diagnostics.",
71+
intensity_mode="sigma_tetradic",
7272
),
7373
DharmaProfile(
7474
name="harloka_bundle_kernel",
@@ -190,22 +190,22 @@ def _seed_default_registries() -> None:
190190
intensity_modes=("sigma_mul", "sigma_add"),
191191
),
192192
TattvaProfile(
193-
name="complex_wave",
193+
name="tetradic_projection",
194194
polarity_count=4,
195-
dharmas=("complex_wave_kernel",),
195+
dharmas=("tetradic_projection_kernel",),
196196
theory_refs=("complex_wave_behaviour",),
197-
description="Four-pole complex wave base with amplitude-phase balance.",
198-
mind_modes=("complex", "wave"),
199-
intensity_modes=("sigma_phase", "sigma_add", "sigma_mul"),
197+
description="Four-pole tetradic base for projection-aware Sigma diagnostics.",
198+
mind_modes=("tetradic", "projection"),
199+
intensity_modes=("sigma_tetradic", "sigma_add", "sigma_mul"),
200200
),
201201
TattvaProfile(
202202
name="quaternion_harloka",
203203
polarity_count=4,
204-
dharmas=("triadic_superposition_kernel", "complex_wave_kernel", "harloka_bundle_kernel"),
204+
dharmas=("triadic_superposition_kernel", "tetradic_projection_kernel", "harloka_bundle_kernel"),
205205
theory_refs=("harloka_bundle",),
206206
description="Harloka bundle linking triadic and tetradic poles.",
207207
mind_modes=("harloka",),
208-
intensity_modes=("sigma_mul", "sigma_phase"),
208+
intensity_modes=("sigma_mul", "sigma_tetradic"),
209209
),
210210
TattvaProfile(
211211
name="relational_visibility",
@@ -223,7 +223,7 @@ def _seed_default_registries() -> None:
223223
theory_refs=("hyper_wave_patterns",),
224224
description="High-rank hyper wave cascades with alternating parity.",
225225
mind_modes=("hyper",),
226-
intensity_modes=("sigma_phase", "sigma_add", "sigma_mul", "sigma_gate"),
226+
intensity_modes=("sigma_tetradic", "sigma_add", "sigma_mul", "sigma_gate"),
227227
),
228228
)
229229
for tattva in tattvas:

devices/communication.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,44 @@ class MultipolarAntenna:
130130
- role: informational label ("tx" or "rx").
131131
- gain: linear gain multiplier (applied after losses).
132132
- loss_db: additional attenuation in dB (applied symmetrically on emit/receive).
133+
- strict_polarity: reject waves whose pole count does not match the tuning.
133134
"""
134135

135-
def __init__(self, *, polarity: int, role: str = "tx", gain: float = 1.0, loss_db: float = 0.0) -> None:
136+
def __init__(
137+
self,
138+
*,
139+
polarity: int,
140+
role: str = "tx",
141+
gain: float = 1.0,
142+
loss_db: float = 0.0,
143+
strict_polarity: bool = True,
144+
) -> None:
136145
self.polarity = int(polarity)
137146
self.role = role
138147
self.gain = float(gain)
139148
self.loss_db = float(loss_db)
149+
self.strict_polarity = bool(strict_polarity)
140150

141151
def _scale(self) -> float:
142152
# Convert dB loss to linear and apply gain
143153
loss_lin = 10.0 ** (-self.loss_db / 20.0) if self.loss_db != 0.0 else 1.0
144154
return self.gain * loss_lin
145155

156+
def is_compatible(self, wave: MultiConjugateFunction) -> bool:
157+
if wave.n_conjugates != self.polarity:
158+
return False
159+
if wave.metadata is None:
160+
return True
161+
return len(wave.metadata.polarity_names) == self.polarity
162+
163+
def _validate_wave(self, wave: MultiConjugateFunction) -> None:
164+
if self.strict_polarity and not self.is_compatible(wave):
165+
raise ValueError(
166+
f"antenna tuned for {self.polarity} poles cannot handle wave with {wave.n_conjugates} poles"
167+
)
168+
146169
def emit(self, wave: MultiConjugateFunction) -> MultiConjugateFunction:
170+
self._validate_wave(wave)
147171
scaled = wave.copy()
148172
scaled.amplitudes = scaled.amplitudes * self._scale()
149173
if scaled.metadata is not None:
@@ -156,6 +180,7 @@ def emit(self, wave: MultiConjugateFunction) -> MultiConjugateFunction:
156180
return scaled
157181

158182
def receive(self, wave: MultiConjugateFunction) -> MultiConjugateFunction:
183+
self._validate_wave(wave)
159184
scaled = wave.copy()
160185
scaled.amplitudes = scaled.amplitudes * self._scale()
161186
if scaled.metadata is not None:

devices/detectors.py

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
from .media import MediaPhantom
1919
from .passport import CascadeStage, StructuralPassport
2020
from .sources import MultipolarOscillator
21+
from .sigma_guard import SigmaGuard
2122
from ..cognition.base import AbstractMind
22-
from ..physics.multipolar_wave import MultiConjugateFunction
23+
from ..physics.multipolar_wave import MultiConjugateFunction, WaveMetadata
2324

2425

2526
class MultipolarMicrophone:
@@ -121,6 +122,7 @@ def __init__(
121122
oscillator: MultipolarOscillator,
122123
*,
123124
key: DynamicKey | None = None,
125+
sigma_guard: SigmaGuard | None = None,
124126
name: str | None = None,
125127
mind: AbstractMind | None = None,
126128
loka: Loka | str | None = None,
@@ -130,6 +132,7 @@ def __init__(
130132
super().__init__(mind=mind, loka=loka, default_rank=desired_rank)
131133
self.oscillator = oscillator
132134
self.key = key
135+
self.sigma_guard = sigma_guard
133136
self.name = name or "MultipolarReceiver"
134137
if self.oscillator.get_polarity() != self.rank:
135138
self.oscillator.set_polarity(self.rank)
@@ -157,6 +160,8 @@ def _build_passport(self) -> StructuralPassport:
157160
notes = ["Receiver mirrors the transmitter's Sigma guard to close the cascade."]
158161
if self.key is not None:
159162
notes.append("Dynamic key retunes polarity and frequency before each capture.")
163+
if self.sigma_guard is not None:
164+
notes.append("SigmaGuard purification is active before decoding.")
160165
return StructuralPassport(
161166
device_name=self.name,
162167
cascade=cascade,
@@ -182,44 +187,104 @@ def set_polarity(self, n_poles: int) -> None:
182187
self._coder = PolarCoder(self._n_polarities, loka=self.loka)
183188
self._sync_passport()
184189

185-
def is_compatible(self, wave: MultiConjugateFunction, *, tol_poles: int = 0, tol_freq: float | None = None) -> bool:
190+
def _expected_loka_names(self) -> set[str]:
191+
return {self.loka.name, self.oscillator.loka.name}
192+
193+
def _expected_polarity_names(self) -> list[str]:
194+
return [p.name for p in self.loka.polarities]
195+
196+
def _metadata_is_compatible(self, wave: MultiConjugateFunction) -> bool:
197+
meta = wave.metadata
198+
if meta is None:
199+
return True
200+
if meta.loka_name not in self._expected_loka_names():
201+
return False
202+
if len(meta.polarity_names) != self.rank:
203+
return False
204+
return list(meta.polarity_names) == self._expected_polarity_names()
205+
206+
def _purify_wave(self, wave: MultiConjugateFunction) -> MultiConjugateFunction:
207+
if self.sigma_guard is None:
208+
return wave.copy()
209+
mv = wave.to_multipolar_value(self.loka)
210+
purified = self.sigma_guard.apply_nx(mv, sections=self.sigma_guard.sections)[-1]
211+
amplitudes = np.asarray(
212+
[purified.coefficients.get(p, 0.0) for p in purified.loka.polarities],
213+
dtype=np.complex128,
214+
)
215+
frequency_hz = self.oscillator.working_frequency
216+
if wave.metadata is not None and wave.metadata.frequency_hz is not None:
217+
frequency_hz = wave.metadata.frequency_hz
218+
metadata = WaveMetadata.from_amplitudes(
219+
amplitudes,
220+
loka_name=purified.loka.name,
221+
polarity_names=[p.name for p in purified.loka.polarities],
222+
frequency_hz=frequency_hz,
223+
)
224+
return MultiConjugateFunction(amplitudes, n_conjugates=self.rank, metadata=metadata)
225+
226+
def is_compatible(
227+
self,
228+
wave: MultiConjugateFunction,
229+
*,
230+
tol_poles: int = 0,
231+
tol_freq: float | None = None,
232+
require_frequency: bool = False,
233+
) -> bool:
186234
poles_ok = abs(wave.n_conjugates - self.rank) <= tol_poles
187235
if not poles_ok:
188236
return False
237+
if not self._metadata_is_compatible(wave):
238+
return False
189239
# Frequency check: only compare against explicit frequency metadata if requested
190-
if tol_freq is None or wave.metadata is None:
240+
if tol_freq is None:
191241
return poles_ok
242+
if wave.metadata is None:
243+
return False if require_frequency else poles_ok
192244
freq_meta = wave.metadata.frequency_hz
193245
if freq_meta is None:
194-
return poles_ok
246+
return False if require_frequency else poles_ok
195247
return abs(freq_meta - self.oscillator.working_frequency) <= tol_freq
196248

197-
def receive(self, wave: MultiConjugateFunction) -> bool:
249+
def receive(self, wave: MultiConjugateFunction, *, purify: bool | None = None) -> bool:
198250
if self.key is not None:
199251
n_pol, freq = self.key.next()
200252
self.set_polarity(n_pol)
201253
self.oscillator.set_frequency(freq)
202254
self.structural_passport.record_metric("configured_polarity", float(self.rank))
203-
tol_freq = 1.0 if self.key is None else None
204-
if not self.is_compatible(wave, tol_freq=tol_freq):
255+
self.structural_passport.record_metric("working_frequency_hz", self.oscillator.working_frequency)
256+
tol_freq = 1.0
257+
if not self.is_compatible(wave, tol_freq=tol_freq, require_frequency=self.key is not None):
205258
return False
206-
self._last_wave = wave
259+
should_purify = self.sigma_guard is not None if purify is None else bool(purify)
260+
self._last_wave = self._purify_wave(wave) if should_purify else wave.copy()
261+
if self._last_wave.metadata is not None:
262+
self.structural_passport.record_metric("last_sigma_norm", self._last_wave.metadata.sigma_norm)
207263
return True
208264

265+
def receive_and_purify(self, wave: MultiConjugateFunction) -> bool:
266+
return self.receive(wave, purify=True)
267+
209268
def last_wave(self) -> Optional[MultiConjugateFunction]:
210269
return self._last_wave
211270

212-
def demodulate(self, wave: Optional[MultiConjugateFunction] = None) -> List[int]:
271+
def demodulate(self, wave: Optional[MultiConjugateFunction] = None, *, purify: bool | None = None) -> List[int]:
213272
target = wave or self._last_wave
214273
if target is None:
215274
return []
275+
should_purify = self.sigma_guard is not None if purify is None else bool(purify)
276+
if should_purify:
277+
target = self._purify_wave(target)
216278
amps = target.amplitudes
217279
if amps.size == 0:
218280
return []
219281
idx = int(np.argmax(np.abs(amps)))
220282
self.structural_passport.record_metric("last_demodulated", float(idx))
221283
return [idx]
222284

285+
def demodulate_clean(self, wave: Optional[MultiConjugateFunction] = None) -> List[int]:
286+
return self.demodulate(wave, purify=True)
287+
223288
def describe_structure(self) -> dict:
224289
self._sync_passport()
225290
return self.structural_passport.to_dict()

physics/composition.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
Note: this is a *direct-sum style* wiring (no shared neutral between layers).
99
For superposition spaces that require a common neutral, keep
1010
``require_shared_neutral=True`` when constructing :class:`SuperpositionalLoka`.
11+
This helper is algebra-only wiring and is not a substitute for the physical
12+
formation/removal cascade used in M/N device flows.
1113
"""
1214

1315
from __future__ import annotations

0 commit comments

Comments
 (0)