Sometimes I see vastly different results from your Python implementation vs your Rust code. Here is a minimal synthetic example--am I doing something wrong?
import numpy as np
from esat.model.sa import SA
from esat.metrics import q_loss
rng = np.random.default_rng(0)
m, n, p = 50, 12, 3
# Synthetic data with known factorization
F_true = rng.uniform(0.1, 1.0, size=(p, n))
G_true = rng.uniform(0.1, 1.0, size=(m, p))
V = G_true @ F_true # W @ H in ESAT convention
U = 0.001 + (0.05 * np.abs(V))
for label, use_rust in [
("Rust (optimized=True) ", True),
("Python (optimized=False)", False),
]:
sa = SA(V=V, U=U, factors=p, method="ls-nmf", seed=42, parallel=False, verbose=False)
sa.optimized = use_rust
sa.train(max_iter=20000, converge_delta=0.01, converge_n=100)
W = sa.W.astype(np.float64)
H = sa.H.astype(np.float64)
Q = q_loss(V, U, W, H)
rel_err = np.linalg.norm(V - W @ H) / np.linalg.norm(V)
print(f"{label}: Q={Q:.2f} rel_err={rel_err:.4f} converged={sa.converged}")
Sometimes I see vastly different results from your Python implementation vs your Rust code. Here is a minimal synthetic example--am I doing something wrong?