Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
MelodyDeterminism - Canonical Determinism Demo (NumPy-only, CPU)
|
| 4 |
Esteso con:
|
| 5 |
- PRNG switch: philox (veloce, GPU-like) / sha256 (indipendente)
|
| 6 |
-
-
|
| 7 |
- Edge test: maschere, ±inf, nan, invariance a shift, idempotenza
|
| 8 |
- dtype selezionabile, benchmark parametrico
|
| 9 |
"""
|
|
@@ -54,7 +54,6 @@ class D:
|
|
| 54 |
- sha256: indipendente da NumPy, più lento
|
| 55 |
"""
|
| 56 |
if PRNG_MODE == "philox":
|
| 57 |
-
# Usa seed+counter come "chiave" del blocco
|
| 58 |
return _philox_random(seed + counter, shape)
|
| 59 |
|
| 60 |
# Fallback SHA256 (deterministico, ma lento)
|
|
@@ -73,21 +72,42 @@ class D:
|
|
| 73 |
arr = np.array(vals, dtype=np.float64).reshape(shape)
|
| 74 |
return arr.astype(np.float32, copy=False)
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
@staticmethod
|
| 77 |
def tree_fixed_reduce(x: np.ndarray) -> np.float64:
|
|
|
|
| 78 |
y = np.asarray(x, dtype=np.float64).reshape(-1)
|
| 79 |
if y.size == 0:
|
| 80 |
return np.float64(0.0)
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
else:
|
| 89 |
-
level = summed
|
| 90 |
-
return np.float64(level[0])
|
| 91 |
|
| 92 |
@staticmethod
|
| 93 |
def kahan_sum(x: np.ndarray) -> np.float64:
|
|
@@ -101,23 +121,13 @@ class D:
|
|
| 101 |
s = t
|
| 102 |
return s
|
| 103 |
|
| 104 |
-
@staticmethod
|
| 105 |
-
def _tree_sum_row(vec64: np.ndarray) -> float:
|
| 106 |
-
v = np.asarray(vec64, dtype=np.float64)
|
| 107 |
-
n = v.size
|
| 108 |
-
m = 1 << (n - 1).bit_length()
|
| 109 |
-
if m != n:
|
| 110 |
-
v = np.pad(v, (0, m - n), constant_values=0.0)
|
| 111 |
-
while v.size > 1:
|
| 112 |
-
v = v[0::2] + v[1::2]
|
| 113 |
-
return float(v[0])
|
| 114 |
-
|
| 115 |
@staticmethod
|
| 116 |
def deterministic_softmax(x: np.ndarray, axis: int = -1, mask: np.ndarray = None, sum_mode: str = "kahan") -> np.ndarray:
|
| 117 |
"""
|
| 118 |
Softmax stabile e deterministica.
|
| 119 |
-
|
| 120 |
-
|
|
|
|
| 121 |
"""
|
| 122 |
x64 = np.asarray(x, dtype=np.float64)
|
| 123 |
if mask is not None:
|
|
@@ -125,16 +135,21 @@ class D:
|
|
| 125 |
if axis < 0:
|
| 126 |
axis = x64.ndim + axis
|
| 127 |
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
z = np.exp(x64 - m)
|
| 130 |
|
|
|
|
| 131 |
z_move = np.moveaxis(z, axis, -1) # [..., L]
|
| 132 |
flat = z_move.reshape(-1, z_move.shape[-1])
|
| 133 |
|
| 134 |
if sum_mode == "tree":
|
| 135 |
sums = np.array([D._tree_sum_row(flat[i]) for i in range(flat.shape[0])], dtype=np.float64)
|
| 136 |
else:
|
| 137 |
-
# Kahan lungo l'ultima dimensione
|
| 138 |
sums = np.zeros((flat.shape[0],), dtype=np.float64)
|
| 139 |
comp = np.zeros((flat.shape[0],), dtype=np.float64)
|
| 140 |
for j in range(flat.shape[-1]):
|
|
@@ -154,10 +169,10 @@ class D:
|
|
| 154 |
def deterministic_categorical(logits: np.ndarray, num_samples: int, seed: int, sum_mode: str = "kahan") -> np.ndarray:
|
| 155 |
"""
|
| 156 |
Sampling deterministico (vectorizzato):
|
| 157 |
-
- softmax canonica (
|
| 158 |
- CDF una volta
|
| 159 |
- U in blocco con PRNG dichiarativo (philox/sha256)
|
| 160 |
-
- searchsorted(..., 'left') ⇒ tie-break deterministico
|
| 161 |
"""
|
| 162 |
x = np.asarray(logits, dtype=np.float64)
|
| 163 |
single = False
|
|
@@ -167,9 +182,13 @@ class D:
|
|
| 167 |
B, V = x.shape
|
| 168 |
|
| 169 |
probs = D.deterministic_softmax(x, axis=-1, sum_mode=sum_mode).astype(np.float64)
|
| 170 |
-
cdf = np.cumsum(probs, axis=-1)
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
U = D.counter_prng(seed, 0, (B, num_samples)).astype(np.float64)
|
|
|
|
| 173 |
idx_rows = [np.searchsorted(cdf[b], U[b], side="left") for b in range(B)]
|
| 174 |
out = np.stack(idx_rows, axis=0).astype(np.int64, copy=False)
|
| 175 |
if single:
|
|
@@ -284,7 +303,7 @@ def run_full_suite(seed: int, n: int, v: int, dtype: str, sum_mode: str) -> Dict
|
|
| 284 |
"shape": [n, v],
|
| 285 |
"dtype": dtype,
|
| 286 |
"prng": PRNG_MODE,
|
| 287 |
-
"note": "NumPy-only canonical ops; philox/sha256 PRNG
|
| 288 |
}
|
| 289 |
return rep
|
| 290 |
|
|
@@ -294,31 +313,26 @@ def run_edge_softmax(seed: int, n: int, v: int, dtype: str, sum_mode: str) -> Di
|
|
| 294 |
"""
|
| 295 |
rng = np.random.default_rng(seed)
|
| 296 |
x = rng.standard_normal((n, v)).astype(np.float64)
|
| 297 |
-
#
|
| 298 |
x[0, 0] = np.inf
|
| 299 |
x[0, 1] = -np.inf
|
| 300 |
x[0, 2] = np.nan
|
| 301 |
-
# Mask:
|
| 302 |
mask = rng.random((n, v)) > 0.2
|
| 303 |
-
# Cast finale
|
| 304 |
x = x.astype(np.float32 if dtype == "float32" else np.float64, copy=False)
|
| 305 |
|
| 306 |
-
# Softmax canonical con maschera
|
| 307 |
p1 = D.deterministic_softmax(x, axis=-1, mask=mask, sum_mode=sum_mode)
|
| 308 |
-
#
|
| 309 |
c = 123.456
|
| 310 |
p2 = D.deterministic_softmax(x + c, axis=-1, mask=mask, sum_mode=sum_mode)
|
| 311 |
inv_shift = bool(np.allclose(p1, p2))
|
| 312 |
-
#
|
| 313 |
p3 = D.deterministic_softmax(p1, axis=-1, mask=np.ones_like(p1, dtype=bool), sum_mode=sum_mode)
|
| 314 |
idempotent = bool(np.allclose(p1, p3))
|
| 315 |
-
#
|
| 316 |
-
|
| 317 |
-
conserve = bool(np.allclose(sums, 1.0))
|
| 318 |
-
|
| 319 |
return {
|
| 320 |
-
"sum_mode": sum_mode,
|
| 321 |
-
"dtype": dtype,
|
| 322 |
"mask_ratio": float(np.mean(mask)),
|
| 323 |
"invariance_shift": inv_shift,
|
| 324 |
"idempotent": idempotent,
|
|
@@ -517,7 +531,7 @@ def run_benchmark_and_save(dtype: str, sum_mode: str, prng_choice: str):
|
|
| 517 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 518 |
gr.Markdown("# MelodyDeterminism - Canonical Determinism Demo (NumPy / CPU)")
|
| 519 |
gr.Markdown(
|
| 520 |
-
"Deterministic ops: reduce (Kahan/Tree), softmax canonica, sampling RNG dichiarativo. "
|
| 521 |
"PRNG: Philox (GPU-like) o SHA256 (indipendente). Edge: maschera, ±inf, nan, shift, idempotenza. "
|
| 522 |
"Benchmark parametrico con overhead%."
|
| 523 |
)
|
|
@@ -541,7 +555,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 541 |
n = gr.Slider(1, 256, step=1, value=8, label="Rows / Batch (n)")
|
| 542 |
v = gr.Slider(2, 4096, step=1, value=32, label="Width / Vocab (v)")
|
| 543 |
dtype = gr.Radio(["float32","float64"], value="float32", label="dtype")
|
| 544 |
-
sum_mode = gr.Radio(["kahan","tree"], value="
|
| 545 |
prng_choice = gr.Radio(["philox","sha256"], value="philox", label="PRNG")
|
| 546 |
run_btn = gr.Button("Run")
|
| 547 |
|
|
@@ -558,7 +572,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 558 |
with gr.Tab("Benchmark"):
|
| 559 |
gr.Markdown("Confronto standard vs deterministico (sampling) con le scelte sotto.")
|
| 560 |
dtype_b = gr.Radio(["float32","float64"], value="float32", label="dtype")
|
| 561 |
-
sum_mode_b = gr.Radio(["kahan","tree"], value="
|
| 562 |
prng_b = gr.Radio(["philox","sha256"], value="philox", label="PRNG")
|
| 563 |
bench_btn = gr.Button("Esegui benchmark")
|
| 564 |
bench_table = gr.Dataframe(
|
|
|
|
| 3 |
MelodyDeterminism - Canonical Determinism Demo (NumPy-only, CPU)
|
| 4 |
Esteso con:
|
| 5 |
- PRNG switch: philox (veloce, GPU-like) / sha256 (indipendente)
|
| 6 |
+
- Softmax canonica con riduzioni pairwise: max tree + sum (kahan/tree)
|
| 7 |
- Edge test: maschere, ±inf, nan, invariance a shift, idempotenza
|
| 8 |
- dtype selezionabile, benchmark parametrico
|
| 9 |
"""
|
|
|
|
| 54 |
- sha256: indipendente da NumPy, più lento
|
| 55 |
"""
|
| 56 |
if PRNG_MODE == "philox":
|
|
|
|
| 57 |
return _philox_random(seed + counter, shape)
|
| 58 |
|
| 59 |
# Fallback SHA256 (deterministico, ma lento)
|
|
|
|
| 72 |
arr = np.array(vals, dtype=np.float64).reshape(shape)
|
| 73 |
return arr.astype(np.float32, copy=False)
|
| 74 |
|
| 75 |
+
@staticmethod
|
| 76 |
+
def _tree_sum_row(vec64: np.ndarray) -> float:
|
| 77 |
+
v = np.asarray(vec64, dtype=np.float64)
|
| 78 |
+
n = v.size
|
| 79 |
+
m = 1 << (n - 1).bit_length()
|
| 80 |
+
if m != n:
|
| 81 |
+
v = np.pad(v, (0, m - n), constant_values=0.0)
|
| 82 |
+
while v.size > 1:
|
| 83 |
+
v = v[0::2] + v[1::2]
|
| 84 |
+
return float(v[0])
|
| 85 |
+
|
| 86 |
+
@staticmethod
|
| 87 |
+
def _tree_max_row(vec64: np.ndarray) -> float:
|
| 88 |
+
"""Riduzione deterministica del massimo (pairwise, GPU-like)."""
|
| 89 |
+
v = np.asarray(vec64, dtype=np.float64)
|
| 90 |
+
n = v.size
|
| 91 |
+
m = 1 << (n - 1).bit_length()
|
| 92 |
+
if m != n:
|
| 93 |
+
v = np.pad(v, (0, m - n), constant_values=-np.inf)
|
| 94 |
+
while v.size > 1:
|
| 95 |
+
v = np.maximum(v[0::2], v[1::2])
|
| 96 |
+
return float(v[0])
|
| 97 |
+
|
| 98 |
@staticmethod
|
| 99 |
def tree_fixed_reduce(x: np.ndarray) -> np.float64:
|
| 100 |
+
"""Somma pairwise deterministica su vettore intero."""
|
| 101 |
y = np.asarray(x, dtype=np.float64).reshape(-1)
|
| 102 |
if y.size == 0:
|
| 103 |
return np.float64(0.0)
|
| 104 |
+
n = y.size
|
| 105 |
+
m = 1 << (n - 1).bit_length()
|
| 106 |
+
if m != n:
|
| 107 |
+
y = np.pad(y, (0, m - n), constant_values=0.0)
|
| 108 |
+
while y.size > 1:
|
| 109 |
+
y = y[0::2] + y[1::2]
|
| 110 |
+
return np.float64(y[0])
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
@staticmethod
|
| 113 |
def kahan_sum(x: np.ndarray) -> np.float64:
|
|
|
|
| 121 |
s = t
|
| 122 |
return s
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
@staticmethod
|
| 125 |
def deterministic_softmax(x: np.ndarray, axis: int = -1, mask: np.ndarray = None, sum_mode: str = "kahan") -> np.ndarray:
|
| 126 |
"""
|
| 127 |
Softmax stabile e deterministica.
|
| 128 |
+
- max pairwise (tree) per asse scelto (GPU-like)
|
| 129 |
+
- sum_mode: 'kahan' (più precisa) | 'tree' (pairwise GPU-like)
|
| 130 |
+
- mask: True = valido, False = mascherato a -inf
|
| 131 |
"""
|
| 132 |
x64 = np.asarray(x, dtype=np.float64)
|
| 133 |
if mask is not None:
|
|
|
|
| 135 |
if axis < 0:
|
| 136 |
axis = x64.ndim + axis
|
| 137 |
|
| 138 |
+
# --- max deterministico pairwise lungo axis ---
|
| 139 |
+
x_move = np.moveaxis(x64, axis, -1) # [..., L]
|
| 140 |
+
flatx = x_move.reshape(-1, x_move.shape[-1])
|
| 141 |
+
m_rows = np.array([D._tree_max_row(flatx[i]) for i in range(flatx.shape[0])], dtype=np.float64)
|
| 142 |
+
m = np.moveaxis(m_rows.reshape(x_move.shape[:-1] + (1,)), -1, axis)
|
| 143 |
+
|
| 144 |
z = np.exp(x64 - m)
|
| 145 |
|
| 146 |
+
# --- sum deterministica (kahan/tree) lungo axis ---
|
| 147 |
z_move = np.moveaxis(z, axis, -1) # [..., L]
|
| 148 |
flat = z_move.reshape(-1, z_move.shape[-1])
|
| 149 |
|
| 150 |
if sum_mode == "tree":
|
| 151 |
sums = np.array([D._tree_sum_row(flat[i]) for i in range(flat.shape[0])], dtype=np.float64)
|
| 152 |
else:
|
|
|
|
| 153 |
sums = np.zeros((flat.shape[0],), dtype=np.float64)
|
| 154 |
comp = np.zeros((flat.shape[0],), dtype=np.float64)
|
| 155 |
for j in range(flat.shape[-1]):
|
|
|
|
| 169 |
def deterministic_categorical(logits: np.ndarray, num_samples: int, seed: int, sum_mode: str = "kahan") -> np.ndarray:
|
| 170 |
"""
|
| 171 |
Sampling deterministico (vectorizzato):
|
| 172 |
+
- softmax canonica (max tree, sum kahan/tree)
|
| 173 |
- CDF una volta
|
| 174 |
- U in blocco con PRNG dichiarativo (philox/sha256)
|
| 175 |
+
- searchsorted(..., 'left') ⇒ tie-break deterministico (min indice)
|
| 176 |
"""
|
| 177 |
x = np.asarray(logits, dtype=np.float64)
|
| 178 |
single = False
|
|
|
|
| 182 |
B, V = x.shape
|
| 183 |
|
| 184 |
probs = D.deterministic_softmax(x, axis=-1, sum_mode=sum_mode).astype(np.float64)
|
| 185 |
+
cdf = np.cumsum(probs, axis=-1)
|
| 186 |
+
# clamp robusto per chiusura [0,1]
|
| 187 |
+
np.clip(cdf, 0.0, 1.0, out=cdf)
|
| 188 |
+
cdf[:, -1] = 1.0
|
| 189 |
|
| 190 |
U = D.counter_prng(seed, 0, (B, num_samples)).astype(np.float64)
|
| 191 |
+
# tie-break deterministico: side='left'
|
| 192 |
idx_rows = [np.searchsorted(cdf[b], U[b], side="left") for b in range(B)]
|
| 193 |
out = np.stack(idx_rows, axis=0).astype(np.int64, copy=False)
|
| 194 |
if single:
|
|
|
|
| 303 |
"shape": [n, v],
|
| 304 |
"dtype": dtype,
|
| 305 |
"prng": PRNG_MODE,
|
| 306 |
+
"note": "NumPy-only canonical ops; philox/sha256 PRNG; softmax max/sum pairwise deterministici.",
|
| 307 |
}
|
| 308 |
return rep
|
| 309 |
|
|
|
|
| 313 |
"""
|
| 314 |
rng = np.random.default_rng(seed)
|
| 315 |
x = rng.standard_normal((n, v)).astype(np.float64)
|
| 316 |
+
# Estremi nella prima riga
|
| 317 |
x[0, 0] = np.inf
|
| 318 |
x[0, 1] = -np.inf
|
| 319 |
x[0, 2] = np.nan
|
| 320 |
+
# Mask: ~80% valido
|
| 321 |
mask = rng.random((n, v)) > 0.2
|
|
|
|
| 322 |
x = x.astype(np.float32 if dtype == "float32" else np.float64, copy=False)
|
| 323 |
|
|
|
|
| 324 |
p1 = D.deterministic_softmax(x, axis=-1, mask=mask, sum_mode=sum_mode)
|
| 325 |
+
# invariance a shift
|
| 326 |
c = 123.456
|
| 327 |
p2 = D.deterministic_softmax(x + c, axis=-1, mask=mask, sum_mode=sum_mode)
|
| 328 |
inv_shift = bool(np.allclose(p1, p2))
|
| 329 |
+
# idempotenza
|
| 330 |
p3 = D.deterministic_softmax(p1, axis=-1, mask=np.ones_like(p1, dtype=bool), sum_mode=sum_mode)
|
| 331 |
idempotent = bool(np.allclose(p1, p3))
|
| 332 |
+
# conserva probabilità
|
| 333 |
+
conserve = bool(np.allclose(np.sum(p1, axis=-1), 1.0))
|
|
|
|
|
|
|
| 334 |
return {
|
| 335 |
+
"sum_mode": sum_mode, "dtype": dtype,
|
|
|
|
| 336 |
"mask_ratio": float(np.mean(mask)),
|
| 337 |
"invariance_shift": inv_shift,
|
| 338 |
"idempotent": idempotent,
|
|
|
|
| 531 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 532 |
gr.Markdown("# MelodyDeterminism - Canonical Determinism Demo (NumPy / CPU)")
|
| 533 |
gr.Markdown(
|
| 534 |
+
"Deterministic ops: reduce (Kahan/Tree), softmax canonica (max tree + sum kahan/tree), sampling RNG dichiarativo. "
|
| 535 |
"PRNG: Philox (GPU-like) o SHA256 (indipendente). Edge: maschera, ±inf, nan, shift, idempotenza. "
|
| 536 |
"Benchmark parametrico con overhead%."
|
| 537 |
)
|
|
|
|
| 555 |
n = gr.Slider(1, 256, step=1, value=8, label="Rows / Batch (n)")
|
| 556 |
v = gr.Slider(2, 4096, step=1, value=32, label="Width / Vocab (v)")
|
| 557 |
dtype = gr.Radio(["float32","float64"], value="float32", label="dtype")
|
| 558 |
+
sum_mode = gr.Radio(["kahan","tree"], value="tree", label="Softmax sum") # default GPU-like
|
| 559 |
prng_choice = gr.Radio(["philox","sha256"], value="philox", label="PRNG")
|
| 560 |
run_btn = gr.Button("Run")
|
| 561 |
|
|
|
|
| 572 |
with gr.Tab("Benchmark"):
|
| 573 |
gr.Markdown("Confronto standard vs deterministico (sampling) con le scelte sotto.")
|
| 574 |
dtype_b = gr.Radio(["float32","float64"], value="float32", label="dtype")
|
| 575 |
+
sum_mode_b = gr.Radio(["kahan","tree"], value="tree", label="Softmax sum") # default GPU-like
|
| 576 |
prng_b = gr.Radio(["philox","sha256"], value="philox", label="PRNG")
|
| 577 |
bench_btn = gr.Button("Esegui benchmark")
|
| 578 |
bench_table = gr.Dataframe(
|