Simo76 commited on
Commit
d8dc6f8
·
verified ·
1 Parent(s): 296a8c2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -46
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
- - Somma softmax: kahan (precisa) / tree (pairwise, GPU-like)
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
- level = y.copy()
82
- while level.size > 1:
83
- n = level.size
84
- pair_n = n // 2
85
- summed = (level[:2*pair_n].reshape(pair_n, 2)).sum(axis=1, dtype=np.float64)
86
- if n % 2 == 1:
87
- level = np.concatenate([summed, level[-1:].astype(np.float64)], axis=0)
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
- sum_mode: 'kahan' (massima precisione) | 'tree' (pairwise GPU-like)
120
- mask: True = valido, False = mascherato a -inf
 
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
- m = np.max(x64, axis=axis, keepdims=True)
 
 
 
 
 
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 (sum_mode: kahan/tree)
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); cdf[:, -1] = 1.0
 
 
 
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 selectable; softmax sum kahan/tree.",
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
- # Inserisci estremi
298
  x[0, 0] = np.inf
299
  x[0, 1] = -np.inf
300
  x[0, 2] = np.nan
301
- # Mask: valida ~80%
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
- # Invariance a shift
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
- # Idempotenza (applicare due volte non cambia)
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
- # Somma a 1
316
- sums = np.sum(p1, axis=-1)
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="kahan", label="Softmax sum")
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="kahan", label="Softmax sum")
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(