SHRED-ROM Tutorial on Kuramoto Sivashinsky#

Open In Colab

Import Libraries#

# PYSHRED
import pyshred
from pyshred import ParametricDataManager, SHRED, ParametricSHREDEngine

# Other helper libraries
import matplotlib.pyplot as plt
from scipy.io import loadmat
import torch
import numpy as np
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload

Load Kuramoto Sivashinsky dataset#

import numpy as np
import urllib.request
# URL of the NPZ file
url = 'https://zenodo.org/records/14524524/files/KuramotoSivashinsky_data.npz?download=1'
# Local filename to save the downloaded file
filename = 'KuramotoSivashinsky_data.npz'
# Download the file from the URL
urllib.request.urlretrieve(url, filename)
# Load the data from the NPZ file
dataset = np.load(filename)

Device Info#

device = pyshred.set_device("auto")
# device = pyshred.set_device("cpu") # force CPU
# device = pyshred.set_device("cuda") # force CUDA
# device = pyshred.set_device("mps") # force MPS
# device = pyshred.set_device("cuda", device_id=0) # force specific GPU
pyshred.device_info()
=== PyShred Device Information ===
Current device: cpu
Device config: DeviceConfig(device_type=<DeviceType.AUTO: 'auto'>, device_id=None, force_cpu=False, warn_on_fallback=True)

Device Availability:
  CUDA available: False
  MPS available: False
  CPU: Always available

Initialize Data Manager#

# Initialize ParametricSHREDDataManager
manager = ParametricDataManager(
    lags = 20,
    train_size = 0.8,
    val_size = 0.1,
    test_size = 0.1,
    )

Add datasets and sensors#

data = dataset['u'] # shape (500, 201, 100)
params = dataset['mu'] # shape (500, 201, 2)


manager.add_data(
    data=data,
    random=3,
    # stationary=[(15,),(30,),(45,)],
    id = 'KS',
    compress = False
)

# add params
manager.add_data(
    data=params,
    # stationary=[(15,),(30,),(45,)],
    id = 'MU',
    compress = False
)

Analyze sensor summary#

manager.sensor_measurements_df
KS-0 KS-1 KS-2
0 -0.434038 -1.148071 0.154032
1 -0.457136 -0.961066 0.332515
2 -0.459894 -0.912785 0.361393
3 -0.446475 -0.873385 0.404951
4 -0.389743 -0.829674 0.485893
... ... ... ...
100495 -0.846195 -1.008426 0.261712
100496 -1.671882 -0.458563 -0.622521
100497 -2.103602 0.638096 -1.591052
100498 -1.971333 1.887684 -2.129965
100499 -1.450100 2.538589 -2.005693

100500 rows × 3 columns

manager.sensor_summary_df
data id sensor_number type loc/traj
0 KS 0 stationary (random) (18,)
1 KS 1 stationary (random) (93,)
2 KS 2 stationary (random) (15,)

Get train, validation, and test set#

train_dataset, val_dataset, test_dataset= manager.prepare()

Initialize SHRED#

When using a ParametricDataManager, ensure latent_forecaster is set to None.

shred = SHRED(sequence_model="LSTM", decoder_model="MLP", latent_forecaster=None)

Fit SHRED#

val_errors = shred.fit(train_dataset=train_dataset, val_dataset=val_dataset, num_epochs=20, sindy_regularization=0)
print('val_errors:', val_errors)
Fitting SHRED...
Epoch 1: Average training loss = 0.031690
Validation MSE (epoch 1): 0.026569
Epoch 2: Average training loss = 0.016993
Validation MSE (epoch 2): 0.015352
Epoch 3: Average training loss = 0.011736
Validation MSE (epoch 3): 0.012691
Epoch 4: Average training loss = 0.009915
Validation MSE (epoch 4): 0.011286
Epoch 5: Average training loss = 0.008901
Validation MSE (epoch 5): 0.010464
Epoch 6: Average training loss = 0.008259
Validation MSE (epoch 6): 0.009664
Epoch 7: Average training loss = 0.007670
Validation MSE (epoch 7): 0.008933
Epoch 8: Average training loss = 0.007126
Validation MSE (epoch 8): 0.008480
Epoch 9: Average training loss = 0.006610
Validation MSE (epoch 9): 0.008143
Epoch 10: Average training loss = 0.006278
Validation MSE (epoch 10): 0.008485
Epoch 11: Average training loss = 0.005930
Validation MSE (epoch 11): 0.007433
Epoch 12: Average training loss = 0.005582
Validation MSE (epoch 12): 0.006999
Epoch 13: Average training loss = 0.005281
Validation MSE (epoch 13): 0.006656
Epoch 14: Average training loss = 0.005139
Validation MSE (epoch 14): 0.006386
Epoch 15: Average training loss = 0.004810
Validation MSE (epoch 15): 0.005950
Epoch 16: Average training loss = 0.004614
Validation MSE (epoch 16): 0.005578
Epoch 17: Average training loss = 0.004570
Validation MSE (epoch 17): 0.005578
Epoch 18: Average training loss = 0.004257
Validation MSE (epoch 18): 0.005292
Epoch 19: Average training loss = 0.004113
Validation MSE (epoch 19): 0.005244
Epoch 20: Average training loss = 0.004088
Validation MSE (epoch 20): 0.005059
val_errors: [0.02656859 0.0153518  0.01269077 0.01128609 0.01046397 0.00966403
 0.00893335 0.00848005 0.00814289 0.00848533 0.00743333 0.00699905
 0.00665622 0.00638625 0.00594996 0.00557757 0.00557818 0.00529238
 0.00524372 0.00505928]

Evaluate SHRED#

train_mse = shred.evaluate(dataset=train_dataset)
val_mse = shred.evaluate(dataset=val_dataset)
test_mse = shred.evaluate(dataset=test_dataset)
print(f"Train MSE: {train_mse:.3f}")
print(f"Val   MSE: {val_mse:.3f}")
print(f"Test  MSE: {test_mse:.3f}")
Train MSE: 0.003
Val   MSE: 0.005
Test  MSE: 0.004

Initialize Parametric SHRED Engine for Downstream Tasks#

engine = ParametricSHREDEngine(manager, shred)

Sensor Measurements to Latent Space#

test_latent_from_sensors = engine.sensor_to_latent(manager.test_sensor_measurements)

Decode Latent Space to Full-State Space#

test_prediction = engine.decode(test_latent_from_sensors) # latent space generated from sensor data

Compare prediction against the truth#

Since both number of trajectories (data.shape[0]) and number of timesteps (data.shape[1]) are both variable, we will leave them combined on the first axis. The remaining axes are all spatial dimensions.

spatial_shape = data.shape[2:]
test_data = data[manager.test_indices]
truth      = test_data.reshape(-1, *spatial_shape)
prediction = test_prediction['KS']

compare_data = [truth, prediction]
titles = ["Test Truth Ground Truth", "Test Prediction"]

vmin, vmax = np.min([d.min() for d in compare_data]), np.max([d.max() for d in compare_data])

fig, axes = plt.subplots(1, 2, figsize=(20, 4), constrained_layout=True)

for ax, d, title in zip(axes, compare_data, titles):
    im = ax.imshow(d, vmin=vmin, vmax=vmax, aspect='auto')
    ax.set(title=title)
    ax.axis("off")

fig.colorbar(im, ax=axes, label="Value", shrink=0.8)
<matplotlib.colorbar.Colorbar at 0x2a69c042da0>

png

Predicted params#

params_prediction = test_prediction['MU']
print(params_prediction.shape)
params_prediction
(10050, 2)





array([[1.502923 , 2.701217 ],
       [1.5866294, 4.104658 ],
       [1.6049353, 4.049966 ],
       ...,
       [1.0774401, 2.5793262],
       [0.9692009, 2.925611 ],
       [1.0723687, 2.6876066]], dtype=float32)

Evaluate MSE on Ground Truth Data#

Since both number of trajectories (data.shape[0]) and number of timesteps (data.shape[1]) are both variable, we will leave them combined on the first axis. The remaining axes are all spatial dimensions.

# Train
t_train = len(manager.train_sensor_measurements)
train_Y = {'KS': data[0:t_train].reshape(-1, *spatial_shape)} # unpack the spatial dimensions
train_error = engine.evaluate(manager.train_sensor_measurements, train_Y)

# Val
t_val = len(manager.test_sensor_measurements)
val_Y = {'KS': data[t_train:t_train+t_val].reshape(-1, *spatial_shape)}
val_error = engine.evaluate(manager.val_sensor_measurements, val_Y)

# Test
t_test = len(manager.test_sensor_measurements)
test_Y = {'KS': data[-t_test:].reshape(-1, *spatial_shape)}
test_error = engine.evaluate(manager.test_sensor_measurements, test_Y)

print('---------- TRAIN ----------')
print(train_error)
print('\n---------- VAL   ----------')
print(val_error)
print('\n---------- TEST  ----------')
print(test_error)
---------- TRAIN ----------
              MSE      RMSE       MAE        R2
dataset                                        
KS       0.085787  0.292894  0.189655  0.934717

---------- VAL   ----------
            MSE      RMSE       MAE        R2
dataset                                      
KS       0.1452  0.381051  0.237308  0.887799

---------- TEST  ----------
             MSE     RMSE       MAE        R2
dataset                                      
KS       0.08774  0.29621  0.185969  0.933305