Splitting and growth

Run each cell in order after installing COATI. Each training run creates a fresh output directory. Synthetic examples test functionality; they are not biological benchmarks.

1. Import

from pathlib import Path
import sys

example_dir = next(parent / "examples" for parent in (Path.cwd(), *Path.cwd().parents)
                   if (parent / "examples" / "synthetic" / "toy_data.py").is_file())
if str(example_dir) not in sys.path:
    sys.path.insert(0, str(example_dir))

from synthetic import toy_data as datasets
import coati as ct
import numpy as np
import matplotlib.pyplot as plt

2. Generate and inspect data

data = datasets.split(n=128, seed=0)
print("Times:", data.times)
print("Primary shapes:", [x.shape for x in data.primary])
print("Secondary shapes:", None if data.secondary is None else [x.shape for x in data.secondary])
Times: [0. 1.]
Primary shapes: [(128, 2), (256, 2)]
Secondary shapes: None
fig, ax = plt.subplots(figsize=(6, 4))
for t, x in zip(data.times, data.primary):
    ax.scatter(x[:, 0], x[:, 1], s=10, alpha=.5, label=f"t={t:g}")
ax.legend()
plt.show()
../_images/f2e04ed2b07b0423ed44f1fd31a1f571892a129c9aac72734044e7ad3bb7028c.png

3. Choose explicit training settings

config = ct.Config(niters=200, seed=0, unbalancedModel=True, alpha_growth=1., mass_loss=True, mass_coefficient=1.)
config.to_dict()
{'time_plot': False,
 'reverseMatching': False,
 'accumulate_training': True,
 'MixSampling': False,
 'seed': 0,
 'gpu': 0,
 'unbalancedModel': True,
 'alpha_growth': 1.0,
 'mass_loss': True,
 'mass_coefficient': 1.0,
 'global_mass': False,
 'hidden_dim': 64,
 'n_layers': 2,
 'activation': 'leaky_relu',
 'niters': 200,
 'lr': 0.001,
 'num_samples': 64,
 'median_n_sample': 128,
 'floor_n_sample': 64,
 'support_points': False,
 'dt': 0.1,
 'mmd_loss': False,
 'density_coefficient': 0.0,
 'pdf_lossMSE': False,
 'pdf_coefficient': 50.0,
 'sinkhorn_pdf': True,
 'energy_coefficient': 1.0,
 'density_topk': 25,
 'density_hinge': 0.01,
 'terminal_density_weight': 1.0,
 'sync_loss': False,
 'sync_weight': 0.0,
 'sync_mmd_loss': False,
 'sync_density_loss': False,
 'sync_pdf_loss': True,
 'map_type': 'kernel',
 'kernel_sigma': 0.01,
 'adaptive_lambda': False,
 'blur_coeff': 0.05,
 'floor_buffer_std': 0.0,
 'lambda_max_pri': 100.0,
 'lambda_max_sec': 100.0,
 'lambda_min_pri': 5.0,
 'lambda_min_sec': 5.0,
 'eta_lambda_pri': 10.0,
 'eta_lambda_sec': 10.0,
 'floor_pri_multiplier': 1.0,
 'floor_sec_multiplier': 1.0,
 'dual_warmup': 500,
 'dual_decay': 1.0,
 'device': 'cpu'}

4. Train

result = ct.fit(data, config)
print(result.output_dir)
/Users/jingfeng/local/project/COATI/outputs/run-5950a32658

5. Integrate trajectories

trajectory = result.predict()
print(trajectory["primary"].shape)
assert np.isfinite(trajectory["primary"]).all()
(41, 128, 2)

6. Inspect metrics and trajectories

Energy is in training units; these toy arrays have not been W2-normalized. Endpoint Sinkhorn is an unweighted geometric diagnostic, including in the growth example.

metrics = result.evaluate()
metrics
{'endpoint_sinkhorn_before': 0.1465214043855667,
 'endpoint_sinkhorn_after': 0.0037763561122119427,
 'kinetic_energy_mean': 0.0011688948143273592,
 'endpoint_sampling_floor': 0.0018356784712523222,
 'predicted_mass_ratio': 0.9675921201705933,
 'observed_count_ratio': 2.0}
result.plot()
plt.show()
../_images/dda74daae7931987ce790c6c85ea082078e28b14c142a2bc4e7961104d61197b.png

7. Reload the saved model

restored = ct.load_result(result.output_dir)
np.testing.assert_array_equal(restored.predict()["primary"], trajectory["primary"])
print("Reload reproduces the trajectory exactly.")
Reload reproduces the trajectory exactly.