| import numpy as np |
| from scipy.fft import dct, idct |
|
|
| class TrajectoryCompressor: |
| """ |
| SVD/DCT Trajectory Compressor (Zymatica Invention 07 Adaptation). |
| Compresses cell trajectories (3D position sequences over time, shape T x 3) |
| using Singular Value Decomposition (SVD) and Discrete Cosine Transform (DCT) |
| to represent the spatial movement patterns compactly. |
| """ |
| def __init__(self, rank=2, k_coef=8): |
| self.rank = rank |
| self.k_coef = k_coef |
|
|
| def compress(self, trajectory): |
| """ |
| Compresses a T x 3 trajectory. |
| Returns: |
| compressed_dict: Dictionary containing compressed DCT coefficients and scale factors. |
| """ |
| T, D = trajectory.shape |
| assert D == 3, "Trajectory must be 3-dimensional (Z, Y, X)" |
| |
| |
| mean_vector = np.mean(trajectory, axis=0) |
| centered_traj = trajectory - mean_vector |
| |
| |
| |
| U, S, Vh = np.linalg.svd(centered_traj, full_matrices=False) |
| |
| |
| r = min(self.rank, D) |
| U_r = U[:, :r] |
| S_r = S[:r] |
| V_r = Vh[:r, :].T |
| |
| |
| sqrt_S = np.sqrt(S_r) |
| U_scaled = U_r * sqrt_S |
| V_scaled = V_r * sqrt_S |
| |
| |
| U_dct_coefs = np.zeros((self.k_coef, r)) |
| for col in range(r): |
| |
| col_dct = dct(U_scaled[:, col], norm='ortho') |
| |
| k_eff = min(self.k_coef, T) |
| U_dct_coefs[:k_eff, col] = col_dct[:k_eff] |
| |
| return { |
| "mean": mean_vector, |
| "U_dct_coefs": U_dct_coefs, |
| "V_scaled": V_scaled, |
| "original_shape": (T, D) |
| } |
|
|
| def decompress(self, compressed_dict): |
| """ |
| Decompresses trajectory coefficients back to T x 3 spatial coordinates. |
| """ |
| mean_vector = compressed_dict["mean"] |
| U_dct_coefs = compressed_dict["U_dct_coefs"] |
| V_scaled = compressed_dict["V_scaled"] |
| T, D = compressed_dict["original_shape"] |
| |
| r = U_dct_coefs.shape[1] |
| |
| |
| U_recon = np.zeros((T, r)) |
| for col in range(r): |
| |
| full_dct = np.zeros(T) |
| k_eff = min(self.k_coef, T) |
| full_dct[:k_eff] = U_dct_coefs[:k_eff, col] |
| U_recon[:, col] = idct(full_dct, norm='ortho') |
| |
| |
| centered_recon = np.dot(U_recon, V_scaled.T) |
| |
| |
| return centered_recon + mean_vector |
|
|
| def test_compression(): |
| print("Testing SVD/DCT Trajectory Compressor...") |
| |
| T = 50 |
| t = np.linspace(0, 4 * np.pi, T) |
| z = t * 1.5 + 5.0 |
| y = np.sin(t) * 10.0 + 100.0 |
| x = np.cos(t) * 10.0 + 100.0 |
| trajectory = np.stack([z, y, x], axis=1) |
| |
| |
| compressor = TrajectoryCompressor(rank=3, k_coef=12) |
| |
| |
| compressed = compressor.compress(trajectory) |
| |
| |
| recon = compressor.decompress(compressed) |
| |
| |
| original_size = trajectory.nbytes |
| |
| stored_floats = 3 + (compressor.k_coef * compressor.rank) + (3 * compressor.rank) |
| compressed_size = stored_floats * 8 |
| |
| mse = np.mean((trajectory - recon) ** 2) |
| cosine_sim = np.dot(trajectory.flatten(), recon.flatten()) / (np.linalg.norm(trajectory) * np.linalg.norm(recon) + 1e-9) |
| |
| print(f" - Original size: {original_size} bytes") |
| print(f" - Compressed parameters: {stored_floats} floats ({compressed_size} bytes)") |
| print(f" - Compression Ratio: {original_size / compressed_size:.2f}x") |
| print(f" - Reconstruction MSE: {mse:.4f}") |
| print(f" - Cosine Fidelity: {cosine_sim * 100:.2f}%") |
| |
| assert mse < 1.0, "MSE reconstruction error is too high!" |
| assert cosine_sim > 0.999, "Fidelity is too low!" |
| print(" - Trajectory SVD/DCT spectral projection: PASSED") |
|
|
| if __name__ == "__main__": |
| test_compression() |
|
|