├── CTPiCS.py ├── CTPiCS2real.py ├── GNN_RNNmodel.py ├── NPRT_HN.py ├── NPRT_SNR.py ├── README.md ├── biomass_data_generating.py ├── biomass_prediction.py ├── empirical_data ├── README.md ├── fine_tuning │ ├── 62.56_1592 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ ├── 63.08_1580 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ ├── 63.11_1609 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ ├── 63.28_1629 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ ├── 78.27_1636 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ ├── 78.53_1651 │ │ ├── cont_para.npy │ │ ├── cont_para_long.npy │ │ ├── trend.pdf │ │ ├── x.npy │ │ └── x_avg_long.npy │ └── README.md ├── linear_inter.py └── test │ ├── 67.53_1623 │ ├── cont_para.npy │ ├── cont_para_long.npy │ ├── trend.pdf │ ├── x.npy │ └── x_avg_long.npy │ ├── 67.90_1621 │ ├── cont_para.npy │ ├── cont_para_long.npy │ ├── h20v08_1700_2000_3600_trend.pdf │ ├── x.npy │ └── x_avg_long.npy │ └── README.md ├── transient_conti_generating.py └── transient_prediction.py /CTPiCS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | import os 5 | import GNN_RNNmodel#gnn_model 6 | import numpy as np 7 | import torch 8 | import torch.nn as nn 9 | import torch.nn.functional as F 10 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 11 | from torch_geometric.nn import GCNConv, GINConv 12 | import scipy.sparse as sp 13 | from torch_geometric.data import Data 14 | import torch_geometric.nn as pyg_nn 15 | from torch_geometric.loader import DataLoader 16 | import networkx as nx 17 | import math 18 | import copy 19 | import random 20 | import scipy.integrate as spi 21 | from sklearn.metrics import mean_squared_error, mean_absolute_error 22 | import time as ti 23 | 24 | 25 | 26 | def create_dataset( dataset_class, data_num, seed): 27 | dataset = [] 28 | print(f'start building {dataset_class}_dataset!') 29 | for system in ['neuron', 'biomass', 'veg_turb']: 30 | filepath = f"data_scaled/{system}/{dataset_class}/" 31 | filenames = os.listdir( filepath ) 32 | seed = seed 33 | data_ids = np.arange(len(filenames)) 34 | np.random.seed(seed) 35 | np.random.shuffle(data_ids) 36 | 37 | for count in range(data_num): 38 | results = np.load( filepath+filenames[data_ids[count]] ) 39 | x = results['x'] 40 | y = results['y'] 41 | 42 | edge_list = np.array([[],[]]) 43 | edge_index = torch.LongTensor(edge_list) 44 | 45 | x = torch.FloatTensor(x) 46 | 47 | y = torch.FloatTensor(y) 48 | 49 | data = Data(x=x, edge_index=edge_index, y=y) 50 | dataset.append(data) 51 | if len(dataset) % 2500 == 0: 52 | print(f"already process {len(dataset)} {system} data for {dataset_class} dataset!") 53 | return dataset 54 | 55 | 56 | 57 | torch.cuda.set_device(1) 58 | 59 | seed = 1024 60 | for time in range(5): 61 | train_dataset = create_dataset( 'train', 10500, seed) 62 | val_test_dataset = create_dataset( 'val_test', 4500, seed) 63 | 64 | random.seed(seed) 65 | random.shuffle(val_test_dataset) 66 | test_dataset = val_test_dataset[-int(len(val_test_dataset)*2/3):] 67 | val_dataset = val_test_dataset[:len(val_test_dataset)-int(len(val_test_dataset)*2/3)] 68 | print(len(train_dataset), len(val_dataset), len(test_dataset)) 69 | 70 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 71 | # model = Net().to(device) 72 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ).to(device) 73 | optimizer = torch.optim.Adam(model.parameters(), lr=0.005) 74 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 75 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 76 | train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) 77 | 78 | model.train() 79 | loss_func = nn.MSELoss() 80 | # loss_compute = weight_MSEloss().to(device) 81 | mae_best, test, best_epoch = 1, 1, 0 82 | loss_epoch = np.zeros(1000) 83 | for epoch in range(1000): 84 | loss_all = 0 85 | 86 | for data in train_loader: 87 | # print(data.edge_index) 88 | # print(data.batch.shape) 89 | # print(data.x.shape) 90 | data = data.to('cuda') 91 | optimizer.zero_grad() 92 | output = model(data) 93 | y = data.y 94 | # w_loss = data.w_loss 95 | # print(label) 96 | loss = loss_func(output, y) 97 | # loss = loss_compute(output, y, w_loss) 98 | loss.backward() 99 | loss_all += loss.item() * len(y) 100 | optimizer.step() 101 | scheduler.step(mae_best) 102 | if (epoch+1)%50==0: 103 | print(f"Epoch{epoch+1}learning rate:{optimizer.param_groups[0]['lr']}" ) 104 | loss_epoch[epoch] = loss_all / len(train_dataset) 105 | 106 | if (epoch+1) % 5 == 0: 107 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 108 | 109 | if (epoch+1) % 10 == 0: 110 | model.eval() 111 | 112 | val_loader = DataLoader(val_dataset, batch_size=500, shuffle=False) 113 | labels, preds = [], [] 114 | for data in val_loader: 115 | label = data.y.numpy() 116 | labels += list(label) 117 | data = data.to('cuda') 118 | pred = model(data).cpu().detach().numpy() 119 | preds += list(pred) 120 | mae = mean_absolute_error(labels, preds) 121 | 122 | test_loader = DataLoader(test_dataset, batch_size=500, shuffle=False) 123 | labels, preds = [], [] 124 | for data in test_loader: 125 | label = data.y.numpy() 126 | labels += list(label) 127 | data = data.to('cuda') 128 | pred = model(data).cpu().detach().numpy() 129 | preds += list(pred) 130 | mae_test = mean_absolute_error(labels, preds) 131 | 132 | print(f'Val dataset, mae loss:', mae) 133 | print(f'Test dataset, mae loss:', mae_test) 134 | if mae < mae_best: 135 | mae_best = mae 136 | test = mae_test 137 | best_epoch = epoch 138 | np.savez(f'prediction/CTPiCS/acc-{time}.npz', label=labels, preds = preds) 139 | torch.save(model.state_dict(), f"prediction/CTPiCS/model_parameter-{time}.pkl") 140 | print(f'Accuracy has been updated, and new model has been saved!') 141 | print(f'times={time}, epoch:{epoch+1}, until now,the best mae loss on Val dataset:{mae_best},Test dataset:{test}, best epoch:{best_epoch+1}') 142 | 143 | model.train() 144 | 145 | # if epoch - best_epoch >= 300: 146 | # print('!!Since best_mae is not updated for too long, break for next training!') 147 | # break 148 | 149 | 150 | # save loss 151 | np.savez(f'prediction/CTPiCS/loss-{time}.npz', loss=loss_epoch) 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /CTPiCS2real.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import os 6 | import GNN_RNNmodel#gnn_model 7 | import numpy as np 8 | import torch 9 | import torch.nn as nn 10 | import torch.nn.functional as F 11 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 12 | from torch_geometric.nn import GCNConv, GINConv 13 | import scipy.sparse as sp 14 | from torch_geometric.data import Data 15 | import torch_geometric.nn as pyg_nn 16 | from torch_geometric.loader import DataLoader 17 | import networkx as nx 18 | import math 19 | import copy 20 | import random 21 | import scipy.integrate as spi 22 | from sklearn.metrics import mean_squared_error, mean_absolute_error 23 | import time as ti 24 | 25 | 26 | 27 | def create_dataset( dataset_class): 28 | dataset = [] 29 | print(f'start building {dataset_class}_dataset!') 30 | 31 | filepath = f"data_scaled/real/{dataset_class}/" 32 | filenames = os.listdir( filepath ) 33 | 34 | for filename in os.listdir( filepath ): 35 | results = np.load( filepath+filename ) 36 | x = results['x'] 37 | y = results['y'] 38 | 39 | edge_list = np.array([[],[]]) 40 | edge_index = torch.LongTensor(edge_list) 41 | 42 | x = torch.FloatTensor(x) 43 | y = torch.FloatTensor(y) 44 | 45 | data = Data(x=x, edge_index=edge_index, y=y) 46 | dataset.append(data) 47 | return dataset 48 | 49 | 50 | 51 | torch.cuda.set_device(0) 52 | 53 | seed = 1024 54 | for time in range(10): 55 | train_dataset = create_dataset( 'train' ) 56 | test_dataset = create_dataset( 'test' ) 57 | 58 | print(len(train_dataset), len(test_dataset)) 59 | 60 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 61 | # model = Net().to(device) 62 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ) 63 | model.load_state_dict(torch.load('prediction/CTPiCS/'+f"model_parameter-5.pkl")) 64 | model.to(device) 65 | 66 | optimizer = torch.optim.Adam(model.parameters(), lr=0.005) 67 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 68 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 69 | train_loader = DataLoader(train_dataset, batch_size=32,shuffle=True) 70 | 71 | 72 | model.train() 73 | loss_func = nn.MSELoss() 74 | # loss_compute = weight_MSEloss().to(device) 75 | mae_best, test, best_epoch = 5, 1, 0 76 | loss_epoch = np.zeros(300) 77 | for epoch in range(300): 78 | loss_all = 0 79 | for data in train_loader: 80 | # print(data.edge_index) 81 | # print(data.batch.shape) 82 | # print(data.x.shape) 83 | data = data.to('cuda') 84 | optimizer.zero_grad() 85 | output = model(data) 86 | y = data.y 87 | # print(label) 88 | loss = loss_func(output, y) 89 | # loss = loss_compute(output, y, w_loss) 90 | loss.backward() 91 | loss_all += loss.item() * len(y) 92 | optimizer.step() 93 | scheduler.step(mae_best) 94 | if (epoch+1)%50==0: 95 | print(f"Epoch{epoch+1}learning rate:{optimizer.param_groups[0]['lr']}" ) 96 | loss_epoch[epoch] = loss_all / len(train_dataset) 97 | 98 | if (epoch+1) % 5 == 0: 99 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 100 | 101 | if (epoch+1) % 10 == 0: 102 | model.eval() 103 | 104 | val_loader = DataLoader(test_dataset, batch_size=len(test_dataset), shuffle=False) 105 | labels, preds = [], [] 106 | for data in val_loader: 107 | label = data.y.numpy() 108 | labels += list(label) 109 | data = data.to('cuda') 110 | pred = model(data).cpu().detach().numpy() 111 | preds += list(pred) 112 | mae = mean_absolute_error(labels, preds) 113 | 114 | print(f'Test dataset, mae loss:', mae) 115 | if mae < mae_best: 116 | mae_best = mae 117 | # test = mae_test 118 | best_epoch = epoch 119 | np.savez(f'prediction/CTPiCS/2real/acc-{time}.npz', label=labels, preds = preds) # hidden_nodes 120 | torch.save(model.state_dict(), f"prediction/CTPiCS/2real/model_parameter-{time}.pkl") 121 | print(f'Accuracy has been updated, and new model has been saved!') 122 | print(f'times={time}, epoch:{epoch+1}, until now,the best mae loss on Test dataset:{mae_best}, best epoch:{best_epoch+1}') 123 | 124 | model.train() 125 | 126 | if epoch - best_epoch >= 200: 127 | print('!!Since best_mae is not updated for too long, break for next training!') 128 | break 129 | 130 | 131 | # save loss 132 | np.savez(f'prediction/CTPiCS/2real/loss-{time}.npz', loss=loss_epoch) 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /GNN_RNNmodel.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch_geometric.nn import GCNConv, GINConv, global_add_pool, global_max_pool, GATConv, SAGEConv 6 | from torch.nn import LSTM, GRU, TransformerEncoder 7 | import torch_geometric.nn as pyg_nn 8 | # from torch_geometric_temporal.nn.recurrent import GConvLSTM, GConvGRU 9 | 10 | 11 | def make_gin_conv(input_dim, out_dim): 12 | return GINConv(nn.Sequential(nn.Linear(input_dim, out_dim), nn.ReLU(), nn.Linear(out_dim, out_dim))) 13 | # return GCNConv(input_dim, out_dim) 14 | 15 | 16 | 17 | class GIN_GRU(nn.Module): 18 | def __init__(self, node_features, hidden_dim, out_dim, num_layers): 19 | super(GIN_GRU, self).__init__() 20 | 21 | # self.recurrent_1 = GConvGRU(node_features, int(node_features/2), 3) 22 | # self.recurrent_2 = GConvGRU(int(node_features/2), int(node_features/4), 3) 23 | 24 | # self.recurrent_1 = LSTM(node_features, hidden_dim, 2) 25 | # self.recurrent_2 = LSTM(hidden_dim, 45, 1) 26 | 27 | self.node_features = node_features 28 | self.hidden_dim = hidden_dim 29 | self.out_dim = out_dim 30 | self.num_layers = num_layers 31 | 32 | self.layers = nn.ModuleList() 33 | self.batch_norms = nn.ModuleList() 34 | # self.fc = nn.Sequential( nn.Linear(hidden_dim, hidden_dim), nn.ReLU() ) 35 | self.bn_last = nn.Linear( 64, 1 ) 36 | 37 | for i in range(num_layers-1): 38 | if i == 0: 39 | self.layers.append(make_gin_conv(node_features, hidden_dim)) 40 | else: 41 | self.layers.append(make_gin_conv(hidden_dim, hidden_dim)) 42 | self.batch_norms.append(nn.BatchNorm1d(hidden_dim)) 43 | 44 | self.layers.append(make_gin_conv(hidden_dim, int(hidden_dim/4))) 45 | self.batch_norms.append(nn.BatchNorm1d(int(hidden_dim/4))) 46 | 47 | self.recurrent = GRU(int(hidden_dim/4), out_dim, 4, dropout=0.1) 48 | # self.recurrent_2 = GRU(int(hidden_dim/2), out_dim, 1) 49 | 50 | project_dim = hidden_dim * num_layers 51 | self.project = nn.Sequential( 52 | nn.Linear(out_dim, hidden_dim), 53 | nn.ReLU(), 54 | nn.Linear(hidden_dim, hidden_dim), 55 | nn.ReLU(), 56 | nn.Linear(hidden_dim, 64), 57 | nn.ReLU()) 58 | 59 | 60 | def forward(self, data): 61 | x, edge_index, batch = data.x, data.edge_index, data.batch 62 | # print(x.shape) 63 | 64 | z = x 65 | zs = [] 66 | # print(z, edge_index, batch) 67 | for conv, bn in zip(self.layers, self.batch_norms): 68 | z = conv(z, edge_index) # , edge_weight 69 | z = F.relu(z) 70 | z = bn(z) 71 | # z = F.dropout(z, p=0.1, training=self.training) 72 | zs.append(z) 73 | # gs = [global_max_pool(z, batch) for z in zs] 74 | # g = torch.cat(gs, dim=1) 75 | z = global_max_pool(z, batch) 76 | 77 | z = z.reshape(-1, 1, int(self.hidden_dim/4)) 78 | z = torch.transpose(z,0,1) 79 | 80 | z = self.recurrent(z) 81 | # print(x.shape) 82 | # z = F.relu(z) 83 | # x = F.dropout(x, training=self.training) 84 | # z = self.recurrent_2(z[0]) 85 | # print(x.shape) 86 | # z = F.relu(z) 87 | 88 | z = torch.transpose(z[0],0,1) 89 | z = z.reshape(-1,self.out_dim) 90 | 91 | 92 | 93 | # g = global_mean_pool(z, batch) 94 | # z, g = [torch.cat(x, dim=1) for x in [zs, gs]] 95 | 96 | # g = self.fc(g) 97 | g = self.project(z) 98 | # output = self.bn_last(torch.cat((g, p.reshape((len(p),1))),1)) 99 | output = self.bn_last(g) 100 | # print(torch.sum(output)) 101 | 102 | return output[:, 0] 103 | 104 | -------------------------------------------------------------------------------- /NPRT_HN.py: -------------------------------------------------------------------------------- 1 | import os 2 | import GNN_RNNmodel#gnn_model 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 8 | from torch_geometric.nn import GCNConv, GINConv 9 | import scipy.sparse as sp 10 | from torch_geometric.data import Data 11 | import torch_geometric.nn as pyg_nn 12 | from torch_geometric.loader import DataLoader 13 | import networkx as nx 14 | import math 15 | import copy 16 | import random 17 | import scipy.integrate as spi 18 | from sklearn.metrics import mean_squared_error, mean_absolute_error 19 | import time as ti 20 | 21 | torch.cuda.set_device(1) 22 | 23 | seed = 1024 24 | for time in range(5): 25 | for removal_ratio in np.arange(0, 1, 0.09): 26 | removal_ratio = round(removal_ratio,2) 27 | 28 | dataset = [] 29 | print('start building dataset!') 30 | counts = np.arange(1000) 31 | np.random.seed(seed) 32 | np.random.shuffle(counts) 33 | flagval, flagtest = 1, 1 34 | for count in counts: 35 | for j in range(55): 36 | if not os.path.exists( f'data_neuron/data{count}_{j}.npz' ): 37 | break 38 | 39 | results = np.load(f'data_neuron/data{count}_{j}.npz') 40 | x = results['x'] 41 | # print(len(x)) 42 | p = results['p'] 43 | y = results['y'] 44 | # w_loss = results['w_loss'] 45 | 46 | 47 | removal_num = int( removal_ratio * len(x) ) 48 | rest = np.random.choice(len(x), len(x)-removal_num, replace=False) 49 | x = x[rest] 50 | 51 | edge_list = np.array([[],[]]) 52 | edge_index = torch.LongTensor(edge_list) 53 | 54 | x = torch.FloatTensor(x) 55 | 56 | y = torch.FloatTensor(y) 57 | p = torch.FloatTensor(p) 58 | 59 | data = Data(x=x, edge_index=edge_index, p=p, y=y) 60 | dataset.append(data) 61 | if len(dataset) % 2000 == 0: 62 | print(f"already process {len(dataset)} data") 63 | if len(dataset) > 8450 and flagval: 64 | datasetvt = copy.deepcopy(dataset) 65 | flagval = 0 66 | print(f"val&test dataset is ok, has {len(datasetvt)} data!") 67 | print(f"already process {len(dataset)} data") 68 | print('dataset is ready!') 69 | 70 | random.seed(seed) 71 | random.shuffle(datasetvt) 72 | test_dataset = datasetvt[-int(len(datasetvt)*2/3):] 73 | val_dataset = datasetvt[:len(datasetvt)-int(len(datasetvt)*2/3)] 74 | print(len(test_dataset), len(val_dataset)) 75 | train_dataset = dataset[len(test_dataset)+len(val_dataset):] 76 | 77 | 78 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 79 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ).to(device) 80 | optimizer = torch.optim.Adam(model.parameters(), lr=0.005) 81 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 82 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 83 | train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) 84 | 85 | 86 | model.train() 87 | loss_func = nn.MSELoss() 88 | # loss_compute = weight_MSEloss().to(device) 89 | mae_best, test, best_epoch = 1, 1, 0 90 | loss_epoch = np.zeros(600) 91 | for epoch in range(600): 92 | loss_all = 0 93 | 94 | for data in train_loader: 95 | # print(data.edge_index) 96 | # print(data.batch.shape) 97 | # print(data.x.shape) 98 | data = data.to('cuda') 99 | optimizer.zero_grad() 100 | output = model(data) 101 | y = data.y 102 | loss = loss_func(output, y) 103 | # loss = loss_compute(output, y, w_loss) 104 | loss.backward() 105 | loss_all += loss.item() * len(y) 106 | optimizer.step() 107 | scheduler.step(mae_best) 108 | if (epoch+1)%50==0: 109 | print(f"Epoch{epoch+1}learning rate:{optimizer.param_groups[0]['lr']}" ) 110 | loss_epoch[epoch] = loss_all / len(train_dataset) 111 | 112 | if (epoch+1) % 5 == 0: 113 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 114 | 115 | 116 | if (epoch+1) % 10 == 0: 117 | model.eval() 118 | 119 | val_loader = DataLoader(val_dataset, batch_size=500, shuffle=False) 120 | labels, preds = [], [] 121 | for data in val_loader: 122 | label = data.y.numpy() 123 | labels += list(label) 124 | data = data.to('cuda') 125 | pred = model(data).cpu().detach().numpy() 126 | preds += list(pred) 127 | mae = mean_absolute_error(labels, preds) 128 | 129 | test_loader = DataLoader(test_dataset, batch_size=500, shuffle=False) 130 | labels, preds = [], [] 131 | for data in test_loader: 132 | label = data.y.numpy() 133 | labels += list(label) 134 | data = data.to('cuda') 135 | pred = model(data).cpu().detach().numpy() 136 | preds += list(pred) 137 | mae_test = mean_absolute_error(labels, preds) 138 | 139 | print(f'Val dataset, mae loss:', mae) 140 | print(f'Test dataset, mae loss:', mae_test) 141 | if mae < mae_best: 142 | mae_best = mae 143 | test = mae_test 144 | best_epoch = epoch 145 | np.savez(f'result/hidden_nodes/acc-{removal_ratio}-{time}.npz', label=labels, preds = preds) 146 | torch.save(model.state_dict(), f"result/hidden_nodes/model_parameter-{removal_ratio}-{time}.pkl") 147 | print(f'Accuracy has been updated, and new model has been saved!') 148 | print(f'times={time}, nodes_removal_ratio={removal_ratio}, epoch:{epoch+1}, until now,the best mae loss on Val dataset:{mae_best},Test dataset:{test}, best epoch:{best_epoch+1}') 149 | 150 | model.train() 151 | 152 | if epoch - best_epoch >= 300: 153 | print('!!Since best_mae is not updated for too long, break for next training!') 154 | break 155 | 156 | 157 | # save loss 158 | np.savez(f'result/hidden_nodes/loss-{removal_ratio}-{time}.npz', loss=loss_epoch) 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /NPRT_SNR.py: -------------------------------------------------------------------------------- 1 | import os 2 | import GNN_RNNmodel#gnn_model 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 8 | from torch_geometric.nn import GCNConv, GINConv 9 | import scipy.sparse as sp 10 | from torch_geometric.data import Data 11 | import torch_geometric.nn as pyg_nn 12 | from torch_geometric.loader import DataLoader 13 | import networkx as nx 14 | import math 15 | import copy 16 | import random 17 | import scipy.integrate as spi 18 | from sklearn.metrics import mean_squared_error, mean_absolute_error 19 | import time as ti 20 | 21 | torch.cuda.set_device(0) 22 | 23 | 24 | seed = 1024 25 | for time in range(5): 26 | for SNR in np.arange(65, 4, -5): 27 | SNR = int(SNR) 28 | 29 | dataset = [] 30 | print('start building dataset!') 31 | counts = np.arange(1000) 32 | np.random.seed(seed) 33 | np.random.shuffle(counts) 34 | flagval, flagtest = 1, 1 35 | for count in counts: 36 | for j in range(55): 37 | if not os.path.exists( f'data_neuron/data{count}_{j}.npz' ): 38 | break 39 | results = np.load(f'data_neuron/data{count}_{j}.npz') 40 | x = results['x'] 41 | p = results['p'] 42 | y = results['y'] 43 | 44 | N1, N2 = x.shape[0], x.shape[1] 45 | noise = np.random.randn(N1, N2) 46 | noise_power = np.sum(noise*noise,axis=1)/N2 47 | Ps = np.sum(x*x,axis=1)/N2 48 | Pn = Ps/10**(SNR/10) 49 | n = noise*np.sqrt(Pn/noise_power).reshape(N1,1) 50 | 51 | x += n 52 | 53 | edge_list = np.array([[],[]]) 54 | edge_index = torch.LongTensor(edge_list) 55 | 56 | x = torch.FloatTensor(x) 57 | 58 | y = torch.FloatTensor(y) 59 | p = torch.FloatTensor(p) 60 | # w_loss = torch.FloatTensor(w_loss) 61 | 62 | data = Data(x=x, edge_index=edge_index, p=p, y=y) 63 | dataset.append(data) 64 | if len(dataset) % 2000 == 0: 65 | print(f"already process {len(dataset)} data") 66 | if len(dataset) > 8450 and flagval: 67 | datasetvt = copy.deepcopy(dataset) 68 | flagval = 0 69 | print(f"val&test dataset is ok, has {len(datasetvt)} data!") 70 | print(f"already process {len(dataset)} data") 71 | print('dataset is ready!') 72 | 73 | random.seed(seed) 74 | random.shuffle(datasetvt) 75 | test_dataset = datasetvt[-int(len(datasetvt)*2/3):] 76 | val_dataset = datasetvt[:len(datasetvt)-int(len(datasetvt)*2/3)] 77 | print(len(test_dataset), len(val_dataset)) 78 | train_dataset = dataset[len(test_dataset)+len(val_dataset):] 79 | 80 | 81 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 82 | # model = Net().to(device) 83 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ).to(device) 84 | optimizer = torch.optim.Adam(model.parameters(), lr=0.005) 85 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 86 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 87 | train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) 88 | 89 | 90 | model.train() 91 | loss_func = nn.MSELoss() 92 | # loss_compute = weight_MSEloss().to(device) 93 | mae_best, test, best_epoch = 1, 1, 0 94 | loss_epoch = np.zeros(600) 95 | for epoch in range(600): 96 | loss_all = 0 97 | 98 | for data in train_loader: 99 | # print(data.edge_index) 100 | # print(data.batch.shape) 101 | # print(data.x.shape) 102 | data = data.to('cuda') 103 | optimizer.zero_grad() 104 | output = model(data) 105 | y = data.y 106 | # w_loss = data.w_loss 107 | # print(label) 108 | loss = loss_func(output, y) 109 | # loss = loss_compute(output, y, w_loss) 110 | loss.backward() 111 | loss_all += loss.item() * len(y) 112 | optimizer.step() 113 | scheduler.step(mae_best) 114 | if (epoch+1)%50==0: 115 | print(f"Epoch{epoch+1}learning rate:{optimizer.param_groups[0]['lr']}" ) 116 | loss_epoch[epoch] = loss_all / len(train_dataset) 117 | 118 | if (epoch+1) % 5 == 0: 119 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 120 | 121 | 122 | if (epoch+1) % 10 == 0: 123 | model.eval() 124 | 125 | val_loader = DataLoader(val_dataset, batch_size=500, shuffle=False) 126 | labels, preds = [], [] 127 | for data in val_loader: 128 | label = data.y.numpy() 129 | labels += list(label) 130 | data = data.to('cuda') 131 | pred = model(data).cpu().detach().numpy() 132 | preds += list(pred) 133 | mae = mean_absolute_error(labels, preds) 134 | 135 | test_loader = DataLoader(test_dataset, batch_size=500, shuffle=False) 136 | labels, preds = [], [] 137 | for data in test_loader: 138 | label = data.y.numpy() 139 | labels += list(label) 140 | data = data.to('cuda') 141 | pred = model(data).cpu().detach().numpy() 142 | preds += list(pred) 143 | mae_test = mean_absolute_error(labels, preds) 144 | 145 | print(f'Val dataset, mae loss:', mae) 146 | print(f'Test dataset, mae loss:', mae_test) 147 | if mae < mae_best: 148 | mae_best = mae 149 | test = mae_test 150 | best_epoch = epoch 151 | np.savez(f'result/SNR/acc-{SNR}-{time}.npz', label=labels, preds = preds) 152 | torch.save(model.state_dict(), f"result/SNR/model_parameter-{SNR}-{time}.pkl") 153 | print(f'Accuracy has been updated, and new model has been saved!') 154 | print(f'times={time}, SNR={SNR}, epoch:{epoch+1}, until now,the best mae loss on Val dataset:{mae_best},Test dataset:{test}, best epoch:{best_epoch+1}') 155 | 156 | model.train() 157 | 158 | if epoch - best_epoch >= 300: 159 | print('!!Since best_mae is not updated for too long, break for next training!') 160 | break 161 | 162 | 163 | # save loss 164 | np.savez(f'result/SNR/loss-{SNR}-{time}.npz', loss=loss_epoch) 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | This repository contains the codes to early predictor for the onset of critical transitions in networked dynamical systems. 3 | 4 | If you use anything in this repository, then please cite: 5 | 6 | Zijia Liu, Xiaozhu Zhang, Xiaolei Ru, Ting-Ting Gao, Jack Murdoch Moore, and Gang Yan, [_Early Predictor for the Onset of Critical Transitions in Networked Dynamical Systems_](https://journals.aps.org/prx/accepted/e2075Kb9Zde1860517e53a2509870f0dbc868ad39), Physical Review X, 2024 7 | 8 | This paper has received prominent recognition, having been featured in _Nature Physics_ through Dr. Karen Mudryk’s article titled [_"Precise Precognition"_](https://www.nature.com/articles/s41567-024-02623-9). Additionally, the American Physical Society (APS) has selected it for highlighting, with Prof. Naoki Masuda penning a captivating viewpoint entitled [_"Predicting Tipping Points in Complex Systems"_](https://physics.aps.org/articles/v17/110). 9 | 10 | ## Files 11 | + `biomass_data_generating.py` - File to generate data for resource biomass systems. 12 | + `biomass_prediction.py` - File for predicting the critical time of sharp decline of resource biomass. 13 | + `GNN_RNNmodel.py` - File of our GIN-GRU deep learning neural network architecture. 14 | + `NPRT_HN.py` - File to test the robustness of our approach against different fractions of incomplete data. 15 | + `NPRT_SNR.py` - File to test the robustness of our approach against different SNR (dB) of observational noise. 16 | + `transient_conti_generating.py` - File to generate data for the circumstance in which the control parameter continuously increases. 17 | + `transient_prediction.py` - File to test the robustness of our approach against transient data. 18 | + `CTPiCS.py` - File to pre-train joint model on massive synthetic data of three systems. 19 | + `CTPiCS2real.py` - File to fine-tune joint model and then predict a new empirical system. 20 | 21 | ## Folder 22 | + `empirical_data` - Data of a new empirical system. 23 | -------------------------------------------------------------------------------- /biomass_data_generating.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | import random 4 | import time 5 | import scipy.integrate as spi 6 | import multiprocessing as mp 7 | import os 8 | 9 | 10 | # os.environ["MKL_NUM_THREADS"] = '4' 11 | # os.environ["NUMEXPR_NUM_THREADS"] = '4' 12 | # os.environ["OMP_NUM_THREADS"] = '4' 13 | 14 | 15 | def scale_free( n, md, seed ): 16 | 17 | np.random.seed(seed) 18 | 19 | m = n * md 20 | gamma_in = 2.5 21 | gamma_out = 2.5 22 | 23 | alpha_in = 1/(gamma_in-1) 24 | alpha_out = 1/(gamma_out-1) 25 | 26 | w_in = np.ones(n) 27 | w_out = np.ones(n) 28 | edges = list() 29 | 30 | for i in range(n): 31 | w_in[i] = 1 / (1 + i)**alpha_in 32 | w_out[i] = 1 / (1 + i)**alpha_out 33 | 34 | 35 | w_in = np.random.permutation(w_in) 36 | w_out = np.random.permutation(w_out) 37 | w_in = w_in / np.sum(w_in) 38 | w_out = w_out / np.sum(w_out) 39 | 40 | l = 0 41 | while l < m: 42 | 43 | s = np.random.choice(range(n), p=w_out) 44 | 45 | t = np.random.choice(range(n), p=w_in) 46 | 47 | if s != t: 48 | edge = (s, t) 49 | if edge not in edges: 50 | edges.append(edge) 51 | l += 1 52 | 53 | # print(edges) 54 | g = nx.DiGraph() 55 | # g.add_nodes_from( range(n) ) 56 | g.add_edges_from( edges ) 57 | mapping = dict(zip(g, range(0, len(g)))) # delete isolated nodes 58 | g = nx.relabel_nodes(g, mapping) # renumber the nodes 59 | 60 | 61 | return g 62 | 63 | 64 | # define dynamics 65 | def diff_func(xr, Ar, r, D, cr): 66 | dd = r*xr*(1-xr/10) - cr*xr**2/(xr**2+1) - D*(np.sum(Ar*(xr.reshape(xr.shape[0],1)-xr),axis=1)) + 0.5*np.random.normal(size=len(xr)) 67 | 68 | return dd 69 | 70 | 71 | def rk4( theta, Ar, r, D, cr, h, diff_func ): # [node, hidden_channel] 72 | # h = 0.01 73 | k1 = diff_func(theta, Ar, r, D, cr) 74 | k2 = diff_func(theta + h * k1 / 2, Ar, r, D, cr) 75 | k3 = diff_func(theta + h * k2 / 2, Ar, r, D, cr) 76 | k4 = diff_func(theta + h * k3, Ar, r, D, cr) 77 | theta = theta + h * (k1 + 2 * k2 + 2 * k3 + k4) / 6 78 | 79 | return theta 80 | 81 | 82 | 83 | def simulation(count): 84 | 85 | time_start = time.time() 86 | _, decimals = str(time_start).split('.') # Take the fractional portion of the current time as seed 87 | seed = int(decimals) 88 | np.random.seed(seed) 89 | 90 | md = 4 + 0.01*np.random.randint(-200,200,size=1) 91 | n = 200 + np.random.randint(-195,200,size=1) 92 | md, n = md[0], int(n) 93 | 94 | g = scale_free( n, md, seed ) 95 | g.remove_nodes_from(list(nx.isolates(g))) 96 | mapping = dict(zip(g, range(0, len(g)))) 97 | g = nx.relabel_nodes(g, mapping) 98 | N = len(g) 99 | 100 | w = np.ones( len(g.edges()) ) 101 | source, target = zip(*g.edges()) 102 | weight_edges = zip( source, target, w ) 103 | g.add_weighted_edges_from( weight_edges ) 104 | 105 | nodes = g.number_of_nodes() 106 | edges = g.number_of_edges() 107 | print(f'graph:{count}, nodes:{nodes}, edges:{edges}, md:{edges/nodes}') 108 | 109 | #generate weight adjacency matrix 110 | A = nx.to_numpy_array( g ) #element: out_link 111 | for s,t, w in g.edges( data=True ): 112 | A[s,t] = w['weight'] 113 | A = A.T #elemnt: in_link 114 | 115 | x = np.random.rand( N ) * 15 #init 116 | r, D = 0.3*np.random.random_sample()+0.7, 0.5*np.random.random_sample()+0.5 117 | print(f'graph:{count}, r:{r}, D:{D}') 118 | 119 | ## Simulate the system 120 | ni = 101 121 | rx = np.zeros((ni,N)) 122 | h = 0.01 123 | t = np.arange(0., 15, h) 124 | c = 1 # grazing rate 125 | dom_eval_re = np.zeros(ni) 126 | 127 | xi = np.zeros(( len(t), N )) 128 | xi[0] = rk4( x, A, r, D, c, h, diff_func ) 129 | for j in range(len(t)-1): 130 | xi[j+1] = rk4( xi[j], A, r, D, c, h, diff_func ) 131 | rx[0] = xi[-1] 132 | 133 | # Compute jacobian matrix 134 | xx = np.tile(rx[0],(N,1)) 135 | jacobian = 0.5*A*xx 136 | row, col = np.diag_indices_from(jacobian) 137 | jacobian[row,col] = 1 - xi[-1]/5 - c*2*xi[-1]/(xi[-1]**2+1)**2 - 0.5*xi[-1]*np.sum(A, axis=1) 138 | 139 | # Compute eigenvalues 140 | evals = np.linalg.eigvals(jacobian) 141 | 142 | # Compute the real part of the dominant eigenvalue (smallest magnitude) 143 | re_evals = [lam.real for lam in evals] 144 | ind_max = np.array(re_evals).argmax() 145 | dom_eval_re[0] = max(re_evals) 146 | 147 | print("Number:", count) 148 | print(f"N:{N}, edges:{len(g.edges())}, md:{edges/nodes}") 149 | print("initial average:", np.average(xi[-1])) 150 | 151 | 152 | ## Compute changing_lambda 153 | t = np.arange(0., 10, h) 154 | for c in np.arange(1.02, 3.01, 0.02): 155 | num = int((c-1)/0.02 - 1) 156 | xi = np.zeros(( len(t), N )) 157 | xi[0] = rk4( rx[num], A, r, D, c, h, diff_func ) 158 | for j in range(len(t)-1): 159 | xi[j+1] = rk4( xi[j], A, r, D, c, h, diff_func ) 160 | 161 | num += 1 162 | rx[num] = xi[-1] 163 | 164 | # Compute jacobian matrix 165 | xx = np.tile(rx[num],(N,1)) 166 | jacobian = 0.5*A*xx 167 | row, col = np.diag_indices_from(jacobian) 168 | jacobian[row,col] = 1 - xi[-1]/5 - c*2*xi[-1]/(xi[-1]**2+1)**2 - 0.5*xi[-1]*np.sum(A, axis=1) 169 | 170 | # Compute eigenvalues 171 | evals = np.linalg.eigvals(jacobian) 172 | 173 | # Compute the real part of the dominant eigenvalue (smallest magnitude) 174 | re_evals = [lam.real for lam in evals] 175 | ind_max = np.array(re_evals).argmax() 176 | dom_eval_re[num] = max(re_evals) 177 | 178 | xarray = np.arange(1, 3.01, 0.02) 179 | rxt = rx.T 180 | time_ode = time.time() 181 | print("Number:", count) 182 | print("final average:", np.average(xi[-1]), f'use {time_ode-time_start} seconds!') 183 | 184 | state = np.average(rxt, axis=0) 185 | A = A.T 186 | 187 | # Save data 188 | save_data_number = 0 189 | tipping_ID = np.argmax(dom_eval_re[20:-(len(state[state<1])-3)]) + 20 190 | if state[tipping_ID+1]<1 and state[tipping_ID-1]>3: 191 | print(f"already found bifurcation in count:{count}, start saving data!!!") 192 | print(f"dev:{dom_eval_re[tipping_ID]}, argdev:{tipping_ID}, Tpoint:{xarray[tipping_ID]}, delta:{state[tipping_ID-1] - state[tipping_ID+1]}") 193 | 194 | for i in range(60): 195 | np.savez( f'data/data{count}_{i}.npz', x = rx[tipping_ID-20-i:tipping_ID-i].T, A = A, y=np.array([xarray[tipping_ID]]), w_loss=np.array([i+1]) ) 196 | if tipping_ID-20-i == 0: 197 | break 198 | save_data_number = i + 1 199 | print(f"count:{count} saves {i+1} data") 200 | else: 201 | print(f"no bifurcation found in count:{count}, dev:{dom_eval_re[tipping_ID]}, argdev:{tipping_ID}, delta:{state[tipping_ID-1] - state[tipping_ID+1]}") 202 | 203 | return save_data_number 204 | 205 | 206 | 207 | def parallel(core=60): 208 | # global p 209 | pool = mp.Pool(core) 210 | 211 | results = [] 212 | for count in np.arange(0, 600, 1): 213 | results.append(pool.apply_async(simulation, args=(count,))) 214 | pool.close() 215 | pool.join() 216 | 217 | listnumber = [] 218 | data_number = 0 219 | 220 | for i in results: 221 | r1 = i.get() 222 | data_number += r1 223 | listnumber.append(r1) 224 | 225 | return data_number, listnumber 226 | # return 227 | 228 | data_number0, listnumber0 = parallel() 229 | print(f"totally generate {data_number0} data!") 230 | listnumber1 = np.array(listnumber0) 231 | np.savez('data_number.npz', data_number = listnumber1) 232 | 233 | 234 | # simulation(0) 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /biomass_prediction.py: -------------------------------------------------------------------------------- 1 | import os 2 | import GNN_RNNmodel#gnn_model 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 8 | from torch_geometric.nn import GCNConv, GINConv 9 | import scipy.sparse as sp 10 | from torch_geometric.data import Data 11 | import torch_geometric.nn as pyg_nn 12 | from torch_geometric.loader import DataLoader 13 | import networkx as nx 14 | import math 15 | import copy 16 | import random 17 | import scipy.integrate as spi 18 | from sklearn.metrics import mean_squared_error, mean_absolute_error 19 | import time as ti 20 | 21 | torch.cuda.set_device(1) 22 | 23 | seed = 1024 24 | for time in range(10): 25 | for removal_ratio in [0]:#np.arange(0, 1, 0.05): 26 | data_num = 0 27 | for count in np.arange(600): 28 | for j in range(60): 29 | if not os.path.exists( f'data/data{count}_{j}.npz' ): 30 | break 31 | data_num += 1 32 | removal_ratio = round(removal_ratio,2) 33 | 34 | dataset = [] 35 | print('start building dataset!') 36 | counts = np.arange(600) 37 | np.random.seed(seed) 38 | np.random.shuffle(counts) 39 | flagval, flagtest = 1, 1 40 | for count in counts: 41 | for j in range(60): 42 | if not os.path.exists( f'data/data{count}_{j}.npz' ): 43 | break 44 | 45 | results = np.load(f'data/data{count}_{j}.npz') 46 | x = results['x'] 47 | y = results['y'] 48 | # w_loss = results['w_loss'] 49 | 50 | removal_num = int( removal_ratio * len(x) ) 51 | rest = np.random.choice(len(x), len(x)-removal_num, replace=False) 52 | x = x[rest] 53 | # print(len(x)) 54 | 55 | edge_list = np.array([[],[]]) 56 | edge_index = torch.LongTensor(edge_list) 57 | 58 | x = torch.FloatTensor(x) 59 | 60 | # Graph label data format conversion 61 | y = torch.FloatTensor(y) 62 | # w_loss = torch.FloatTensor(w_loss) 63 | 64 | data = Data(x=x, edge_index=edge_index, y=y) 65 | dataset.append(data) 66 | if len(dataset) % 2500 == 0: 67 | print(f"already process {len(dataset)} data") 68 | if len(dataset) > (data_num*0.3-30) and flagval: 69 | datasetvt = copy.deepcopy(dataset) 70 | flagval = 0 71 | print(f"val&test dataset is ok, has {len(datasetvt)} data!") 72 | print(f"already process {len(dataset)} data") 73 | print('dataset is ready!') 74 | 75 | # split dataset 76 | random.seed(seed) 77 | random.shuffle(datasetvt) 78 | test_dataset = datasetvt[-int(len(datasetvt)*2/3):] 79 | val_dataset = datasetvt[:len(datasetvt)-int(len(datasetvt)*2/3)] 80 | print(len(test_dataset), len(val_dataset)) 81 | train_dataset = dataset[len(test_dataset)+len(val_dataset):] 82 | 83 | 84 | # build model 85 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 86 | # model = Net().to(device) 87 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ).to(device) 88 | optimizer = torch.optim.Adam(model.parameters(), lr=0.001) 89 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 90 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 91 | train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) 92 | 93 | 94 | model.train() 95 | loss_func = nn.MSELoss() 96 | # loss_compute = weight_MSEloss().to(device) 97 | mae_best, test, best_epoch = 1, 1, 0 98 | loss_epoch = np.zeros(700) 99 | for epoch in range(700): 100 | loss_all = 0 101 | for data in train_loader: 102 | # print(data.edge_index) 103 | # print(data.batch.shape) 104 | # print(data.x.shape) 105 | data = data.to('cuda') 106 | optimizer.zero_grad() 107 | output = model(data) 108 | y = data.y 109 | # w_loss = data.w_loss 110 | # print(label) 111 | loss = loss_func(output, y) 112 | # loss = loss_compute(output, y, w_loss) 113 | loss.backward() 114 | loss_all += loss.item() * len(y) 115 | optimizer.step() 116 | scheduler.step(mae_best) 117 | if (epoch+1)%50==0: # len(data)<128 & epoch%50==0: 118 | # print("Epoch%d learning rate:%f" % (epoch, optimizer.param_groups[0]['lr'])) 119 | print(f"Epoch{epoch+1}learning rate:{optimizer.param_groups[0]['lr']}" ) 120 | loss_epoch[epoch] = loss_all / len(train_dataset) 121 | # tmp = (loss_all / len(train_dataset)) 122 | 123 | if (epoch+1) % 5 == 0: 124 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 125 | 126 | 127 | if (epoch+1) % 10 == 0: 128 | model.eval() 129 | 130 | val_loader = DataLoader(val_dataset, batch_size=500, shuffle=False) 131 | labels, preds = [], [] 132 | for data in val_loader: 133 | label = data.y.numpy() 134 | labels += list(label) 135 | data = data.to('cuda') 136 | pred = model(data).cpu().detach().numpy() 137 | preds += list(pred) 138 | mae = mean_absolute_error(labels, preds) 139 | 140 | test_loader = DataLoader(test_dataset, batch_size=500, shuffle=False) 141 | labels, preds = [], [] 142 | for data in test_loader: 143 | label = data.y.numpy() 144 | labels += list(label) 145 | data = data.to('cuda') 146 | pred = model(data).cpu().detach().numpy() 147 | preds += list(pred) 148 | mae_test = mean_absolute_error(labels, preds) 149 | 150 | print(f'Val dataset, mae loss:', mae) 151 | print(f'Test dataset, mae loss:', mae_test) 152 | if mae < mae_best: 153 | mae_best = mae 154 | test = mae_test 155 | best_epoch = epoch 156 | np.savez(f'prediction/acc-{removal_ratio}-{time}.npz', label=labels, preds = preds) 157 | torch.save(model.state_dict(), f"prediction/model_parameter-{removal_ratio}-{time}.pkl") 158 | print(f'Accuracy has been updated, and new model has been saved!') 159 | print(f'times={time}, nodes_removal_ratio={removal_ratio}, epoch:{epoch+1}, until now,the best mae loss on Val dataset:{mae_best},Test dataset:{test}, best epoch:{best_epoch+1}') 160 | 161 | model.train() 162 | 163 | # if epoch - best_epoch >= 300: 164 | # print('!!Since best_mae is not updated for too long, break for next training!') 165 | # break 166 | 167 | 168 | # save loss 169 | np.savez(f'prediction/loss-{removal_ratio}-{time}.npz', loss=loss_epoch) 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /empirical_data/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | This is a folder containing empirical data of vegetated ecosystems in Africa. 3 | 4 | The data were laboriously extracted from the original global dataset, which follows the details in the paper ([_Early Predictor_](https://journals.aps.org/prx/accepted/e2075Kb9Zde1860517e53a2509870f0dbc868ad39)). 5 | If you use these data in a publication, project, etc., then please cite: 6 | 7 | 1. [Early Predictor](https://journals.aps.org/prx/accepted/e2075Kb9Zde1860517e53a2509870f0dbc868ad39): Zijia Liu, Xiaozhu Zhang, Xiaolei Ru, Ting-Ting Gao, Jack Murdoch Moore, and Gang Yan, _Early Predictor for the Onset of Critical Transitions in Networked Dynamical Systems_, Physical Review X, 2024 8 | 9 | 2. [Tree coverage](https://doi.org/10.5067/MODIS/MOD44B.061): DiMiceli, C., R. Sohlberg, J. Townshend. MODIS/Terra Vegetation Continuous Fields Yearly L3 Global 250m SIN Grid V061. 2022, distributed by NASA EOSDIS Land Processes DAAC. Accessed YYYY-MM-DD 10 | 11 | 3. [Rainfall](https://doi.org/10.5067/TRMM/TMPA/MONTH/7): Tropical Rainfall Measuring Mission (TRMM) (2011), TRMM (TMPA/3B43) Rainfall Estimate L3 1 month 0.25 degree x 0.25 degree V7, Greenbelt, MD, Goddard Earth Sciences Data and Information Services Center (GES DISC), Accessed: YYYY-MM-DD 12 | 13 | ## Folders 14 | + `fine_tuning` - Data used to fine-tune on pretrained model. 15 | + `test` - Data of a new district. 16 | 17 | ## File 18 | + `linear_inter.py` - Code to linearly interpolate the raw data (in the folders) for early predicting. 19 | -------------------------------------------------------------------------------- /empirical_data/fine_tuning/62.56_1592/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/62.56_1592/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/62.56_1592/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/62.56_1592/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/62.56_1592/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/62.56_1592/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/62.56_1592/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/62.56_1592/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/62.56_1592/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/62.56_1592/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.08_1580/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.08_1580/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.08_1580/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.08_1580/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.08_1580/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.08_1580/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.08_1580/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.08_1580/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.08_1580/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.08_1580/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.11_1609/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.11_1609/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.11_1609/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.11_1609/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.11_1609/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.11_1609/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.11_1609/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.11_1609/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.11_1609/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.11_1609/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.28_1629/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.28_1629/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.28_1629/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.28_1629/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.28_1629/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.28_1629/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.28_1629/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.28_1629/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/63.28_1629/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/63.28_1629/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.27_1636/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.27_1636/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.27_1636/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.27_1636/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.27_1636/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.27_1636/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.27_1636/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.27_1636/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.27_1636/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.27_1636/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.53_1651/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.53_1651/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.53_1651/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.53_1651/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.53_1651/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.53_1651/trend.pdf -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.53_1651/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.53_1651/x.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/78.53_1651/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/fine_tuning/78.53_1651/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/fine_tuning/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Each folder contains one subset of data within two districts. The folder_name is the critical value of average tree coverage and annual rainfall. 3 | Files in each folder contains: 4 | + `x.npy` - Time series of the tree coverage before critical transition. 5 | + `cont_para.npy` - Time series of the annual rainfall (control parameter) before critical transition. 6 | + `x_avg_long.npy` - Time series of the average tree coverage. 7 | + `cont_para_long.npy` - Time series of the annual rainfall (control parameter). 8 | + `trend.pdf` - Figure of the average tree cover versus the annual rainfall. 9 | -------------------------------------------------------------------------------- /empirical_data/linear_inter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | import os 8 | import time 9 | import networkx as nx 10 | import numpy as np 11 | import random 12 | import scipy.integrate as spi 13 | import multiprocessing as mp 14 | from pyhdf.SD import * 15 | from collections import defaultdict 16 | # import torch 17 | # from torch_geometric.utils import dense_to_sparse 18 | import matplotlib.pyplot as plt 19 | from matplotlib.ticker import MultipleLocator, FormatStrFormatter 20 | 21 | 22 | 23 | location, district = 'Af', 'h20v08' 24 | filePath = f'real_data/{location}/{district}/' 25 | filenames = os.listdir(filePath) 26 | 27 | transdict = defaultdict(list) 28 | for file in filenames: 29 | # print(file) 30 | trans = file.split('_') 31 | transdict[ (int(trans[1]), int(trans[2])) ].append(file) 32 | 33 | cp_linear_step, w_width = 2.5, 20 34 | keys = list(transdict.keys()) # [ key for key in transdict] 35 | for i in range(len(keys)): 36 | for j in range( len(transdict[keys[i]]) ): 37 | x = np.load( filePath+f'{transdict[keys[i]][j]}/SI/x.npy' ) 38 | cont_para = np.load( filePath+f'{transdict[keys[i]][j]}/SI/cont_para.npy' ) 39 | cp_linear = np.arange(cont_para[-1], cont_para[0], cp_linear_step)[::-1] 40 | 41 | len_cp = len(cp_linear) 42 | x_linear = np.zeros(( len_cp, len(x[0]) )) 43 | for k in range(len_cp-1): 44 | inter_id = np.where(cont_para > cp_linear[k])[0][-1] 45 | x_linear[k] = (cp_linear[k]-cont_para[inter_id])/(cont_para[inter_id+1]-cont_para[inter_id]) * (x[inter_id+1]-x[inter_id]) + x[inter_id] 46 | 47 | x_linear[-1] = x[-1] 48 | save_path = f'real_data/transects/{i}_{j}/' 49 | if not os.path.exists( save_path ): 50 | os.makedirs( save_path ) 51 | for lt in range(1, len_cp+1-w_width): 52 | np.savez( save_path+f'{district}_{transdict[keys[i]][j]}_{lt}.npz', ts = x_linear[-lt-w_width:-lt].T, y = cont_para[-1], cp_max = cp_linear[0] ) 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /empirical_data/test/67.53_1623/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.53_1623/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/test/67.53_1623/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.53_1623/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/test/67.53_1623/trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.53_1623/trend.pdf -------------------------------------------------------------------------------- /empirical_data/test/67.53_1623/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.53_1623/x.npy -------------------------------------------------------------------------------- /empirical_data/test/67.53_1623/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.53_1623/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/test/67.90_1621/cont_para.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.90_1621/cont_para.npy -------------------------------------------------------------------------------- /empirical_data/test/67.90_1621/cont_para_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.90_1621/cont_para_long.npy -------------------------------------------------------------------------------- /empirical_data/test/67.90_1621/h20v08_1700_2000_3600_trend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.90_1621/h20v08_1700_2000_3600_trend.pdf -------------------------------------------------------------------------------- /empirical_data/test/67.90_1621/x.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.90_1621/x.npy -------------------------------------------------------------------------------- /empirical_data/test/67.90_1621/x_avg_long.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-serious/tipping-predictor/1c3b04bfac6febd677d63e4c717d336418dc3ce2/empirical_data/test/67.90_1621/x_avg_long.npy -------------------------------------------------------------------------------- /empirical_data/test/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Each folder contains one subset of data within a new district. The folder_name is the critical value of average tree coverage and annual rainfall. 3 | Files in each folder contains: 4 | + `x.npy` - Time series of the tree coverage before critical transition. 5 | + `cont_para.npy` - Time series of the annual rainfall (control parameter) before critical transition. 6 | + `x_avg_long.npy` - Time series of the average tree coverage. 7 | + `cont_para_long.npy` - Time series of the annual rainfall (control parameter). 8 | + `trend.pdf` - Figure of the average tree cover versus the annual rainfall. 9 | -------------------------------------------------------------------------------- /transient_conti_generating.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | import random 4 | import time 5 | import scipy.integrate as spi 6 | import multiprocessing as mp 7 | import os 8 | 9 | # os.environ["MKL_NUM_THREADS"] = '4' 10 | # os.environ["NUMEXPR_NUM_THREADS"] = '4' 11 | # os.environ["OMP_NUM_THREADS"] = '4' 12 | 13 | # Define network and hyper-parameters 14 | # md = 5 15 | # n = 500 16 | mu, delta = 10, 1 17 | 18 | # define dynamics 19 | def diff(xr, Ar): 20 | dd = -xr + Ar.dot(1 / (1 + np.exp(mu-delta*xr))) 21 | return dd 22 | 23 | def rk4( theta, Ar, h, diff ): # [node, hidden_channel] 24 | # h = 0.01 25 | k1 = diff(theta, Ar) 26 | k2 = diff(theta + h * k1 / 2, Ar) 27 | k3 = diff(theta + h * k2 / 2, Ar) 28 | k4 = diff(theta + h * k3, Ar) 29 | theta = theta + h * (k1 + 2 * k2 + 2 * k3 + k4) / 6 30 | 31 | return theta 32 | 33 | 34 | def simulation(count): 35 | 36 | time_start = time.time() 37 | _, decimals = str(time_start).split('.') 38 | seed = int(decimals) 39 | np.random.seed(seed) 40 | 41 | md = 4.5 + 0.01*np.random.randint(-150,150,size=1) 42 | n = 100 + np.random.randint(-90, 100,size=1) 43 | md, n = md[0], int(n) 44 | 45 | # g = scale_free( n, md, seed ) 46 | g = nx.erdos_renyi_graph( n=n, p=md/(n-1), directed=True, seed=int(seed) ) # << n 47 | g.remove_nodes_from(list(nx.isolates(g))) 48 | mapping = dict(zip(g, range(0, len(g)))) 49 | g = nx.relabel_nodes(g, mapping) 50 | 51 | w = np.random.rand( len(g.edges()) ) + (np.random.rand()*10+10) #uniform (0,1)+0.2, mean value=0.5+0.2=0.7; w>0 52 | 53 | source, target = zip(*g.edges()) 54 | weight_edges = zip( source, target, w ) 55 | g.add_weighted_edges_from( weight_edges ) 56 | N = len(g) 57 | 58 | #generate weight adjacency matrix 59 | A = nx.to_numpy_array( g ) #element: out_link 60 | for s,t, w in g.edges( data=True ): 61 | A[s,t] = w['weight'] 62 | A = A.T #elemnt: in_link 63 | 64 | x = np.random.rand( len(g.nodes()) ) * 10 + 8 #init 65 | 66 | ## Simulate the system 67 | # ni = 101 68 | ni = 100 69 | rx = np.zeros((ni,N)) 70 | # t = np.arange(0., 3, 0.01) 71 | h = 0.01 72 | t = np.arange(0., 15, h) 73 | p = 1 # control the weight 74 | dom_eval_re = np.zeros(ni) 75 | 76 | # xi, info_dict = spi.odeint(diff, x, t, args=(A, mu, delta), 77 | # full_output=True, 78 | # hmin=1e-14, 79 | # mxhnil=0) 80 | xi = np.zeros(( len(t), N )) 81 | xi[0] = rk4( x, A, h, diff ) 82 | pi = np.arange(p, p-0.01, -0.01/len(t)) 83 | for j in range(len(t)-1): 84 | At = A * pi[j+1] 85 | xi[j+1] = rk4( xi[j], At, h, diff ) 86 | 87 | rx[0] = xi[-1] 88 | 89 | # Compute jacobian matrix 90 | xx = np.tile(rx[0],(N,1)) 91 | # jacobian = A*(np.exp(mu-xx)/(1+np.exp(mu-xx))**2) 92 | jacobian = At*(np.exp(mu-xx)/(1+np.exp(mu-xx))**2) 93 | row, col = np.diag_indices_from(jacobian) 94 | jacobian[row,col] = -1 95 | 96 | # Compute eigenvalues 97 | evals = np.linalg.eigvals(jacobian) 98 | 99 | # Compute the real part of the dominant eigenvalue (smallest magnitude) 100 | re_evals = [lam.real for lam in evals] 101 | ind_max = np.array(re_evals).argmax() 102 | dom_eval_re[0] = max(re_evals) 103 | 104 | print("Number:", count) 105 | print(f"N:{N}, edges:{len(g.edges())}, md:{md}") 106 | print("initial average:", np.average(xi[-1])) 107 | 108 | 109 | ## Compute changing_lambda(descending order) 110 | # t = np.arange(0., 3, 0.01) 111 | t = np.arange(0., 10, h) 112 | for p in np.arange(0.99, -0.01, -0.01): 113 | i = int((1-p)/0.01-1) 114 | 115 | # Ai = A * p # not changing the original data 116 | # xi, info_dict = spi.odeint(diff, rx[i], t, args=(Ai, mu, delta), 117 | # full_output=True, 118 | # hmin=1e-14, 119 | # mxhnil=0) 120 | pi = np.arange(p, p-0.01, -0.01/len(t)) 121 | xi = np.zeros(( len(t), N )) 122 | xi[0] = rk4( rx[i], A*pi[0], h, diff ) 123 | for j in range(len(t)-1): 124 | At = A * pi[j+1] 125 | xi[j+1] = rk4( xi[j], At, h, diff ) 126 | i = i+1 127 | rx[i] = xi[-1] 128 | 129 | # Compute jacobian matrix 130 | xx = np.tile(rx[i],(N,1)) 131 | # jacobian = Ai*(np.exp(mu-xx)/(1+np.exp(mu-xx))**2) 132 | jacobian = At*(np.exp(mu-xx)/(1+np.exp(mu-xx))**2) 133 | row, col = np.diag_indices_from(jacobian) 134 | jacobian[row,col] = -1 135 | 136 | # Compute eigenvalues 137 | evals = np.linalg.eigvals(jacobian) 138 | 139 | # Compute the real part of the dominant eigenvalue (smallest magnitude) 140 | re_evals = [lam.real for lam in evals] 141 | ind_max = np.array(re_evals).argmax() 142 | dom_eval_re[i] = max(re_evals) 143 | if i == (ni-1): 144 | break 145 | 146 | # xarray = np.arange(0, 1.01, 0.01) 147 | rxt = rx.T 148 | print("Number:", count) 149 | print("final average:", np.average(xi[-1])) 150 | 151 | state = np.average(rxt, axis=0) 152 | A = A.T 153 | 154 | m = np.argmax(dom_eval_re[20:95]) 155 | # pt = (m+20)*0.01 156 | pt = (m+20+1)*0.01 157 | save_data_number = 0 158 | 159 | print(f"Start saving data!") 160 | print(f"dev:{dom_eval_re[20:95].max()}, argdev:{m+20}, delta:{state[m+20] - state[m+21]}") 161 | for i in range(55): 162 | # p = pt+i*0.01 163 | np.savez(f'transient_conti/data{count}_{i}.npz', x = rx[m-i:m+20-i].T, y=np.array([pt]), w_loss=np.array([i+1])) 164 | if m-i == 0: 165 | break 166 | save_data_number = i + 1 167 | print(f"count: {count} saves {i+1} data") 168 | 169 | 170 | return save_data_number 171 | 172 | 173 | def parallel(core=60): 174 | # global p 175 | pool = mp.Pool(core) 176 | 177 | results = [] 178 | for count in np.arange(0, 1000, 1): 179 | results.append(pool.apply_async(simulation, args=(count,))) 180 | pool.close() 181 | pool.join() 182 | 183 | listnumber = [] 184 | data_number = 0 185 | 186 | for i in results: 187 | r1 = i.get() 188 | data_number += r1 189 | listnumber.append(r1) 190 | 191 | return data_number, listnumber 192 | # return 193 | 194 | 195 | data_number0, listnumber0 = parallel() 196 | print(f"totally generate {data_number0} data!") 197 | listnumber1 = np.array(listnumber0) 198 | np.savez('transient_conti/data_number.npz', data_number = listnumber1) 199 | 200 | 201 | # simulation(0) 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /transient_prediction.py: -------------------------------------------------------------------------------- 1 | import time as ti 2 | import os 3 | import GNN_RNNmodel#gnn_model 4 | import numpy as np 5 | import torch 6 | import torch.nn as nn 7 | import torch.nn.functional as F 8 | from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau 9 | from torch_geometric.nn import GCNConv, GINConv 10 | import scipy.sparse as sp 11 | from torch_geometric.data import Data 12 | import torch_geometric.nn as pyg_nn 13 | from torch_geometric.loader import DataLoader 14 | import networkx as nx 15 | import math 16 | import copy 17 | import random 18 | import scipy.integrate as spi 19 | from sklearn.metrics import mean_squared_error, mean_absolute_error 20 | 21 | 22 | torch.cuda.set_device(1) 23 | 24 | seed = 1024 25 | for time in range(10): 26 | data_num = 0 27 | for count in np.arange(600): 28 | for j in range(55): 29 | if not os.path.exists( f'transient_conti/data{count}_{j}.npz' ): 30 | break 31 | data_num += 1 32 | 33 | dataset = [] 34 | print('start building dataset!') 35 | counts = np.arange(600) 36 | np.random.seed(seed) 37 | np.random.shuffle(counts) 38 | flagval, flagtest = 1, 1 39 | for count in counts: 40 | for j in range(55): 41 | if not os.path.exists( f'transient_conti/data{count}_{j}.npz' ): 42 | break 43 | 44 | results = np.load(f'transient_conti/data{count}_{j}.npz') 45 | x = results['x'] 46 | y = results['y'] 47 | 48 | edge_list = np.array([[],[]]) 49 | edge_index = torch.LongTensor(edge_list) 50 | 51 | x = torch.FloatTensor(x) 52 | y = torch.FloatTensor(y) 53 | 54 | data = Data(x=x, edge_index=edge_index, y=y) 55 | dataset.append(data) 56 | if len(dataset) % 2000 == 0: 57 | print(f"already process {len(dataset)} data") 58 | if len(dataset) > (data_num*0.3-30) and flagval: 59 | datasetvt = copy.deepcopy(dataset) 60 | flagval = 0 61 | print(f"val&test dataset is ok, has {len(datasetvt)} data!") 62 | print(f"already process {len(dataset)} data") 63 | print('dataset is ready!') 64 | 65 | random.seed(seed) 66 | random.shuffle(datasetvt) 67 | test_dataset = datasetvt[-int(len(datasetvt)*2/3):] 68 | val_dataset = datasetvt[:len(datasetvt)-int(len(datasetvt)*2/3)] 69 | print(len(test_dataset), len(val_dataset)) 70 | train_dataset = dataset[len(test_dataset)+len(val_dataset):] 71 | 72 | 73 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 74 | # model = Net().to(device) 75 | model = GNN_RNNmodel.GIN_GRU( node_features= 20 , hidden_dim = 256, out_dim = 32, num_layers = 6 ).to(device) 76 | optimizer = torch.optim.Adam(model.parameters(), lr=0.005) 77 | # scheduler = StepLR(optimizer, step_size=150*math.ceil((len(train_dataset))/256), gamma=0.5) 78 | scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.6, patience=50, verbose=True, threshold=1e-5, min_lr=1e-5) # patience=50*math.ceil((len(train_dataset))/256) 79 | train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) 80 | 81 | 82 | model.train() 83 | loss_func = nn.MSELoss() 84 | # loss_compute = weight_MSEloss().to(device) 85 | mae_best, test, best_epoch = 1, 1, 0 86 | loss_epoch = np.zeros(600) 87 | for epoch in range(600): 88 | loss_all = 0 89 | 90 | for data in train_loader: 91 | # print(data.edge_index) 92 | # print(data.batch.shape) 93 | # print(data.x.shape) 94 | data = data.to('cuda') 95 | optimizer.zero_grad() 96 | output = model(data) 97 | y = data.y 98 | # w_loss = data.w_loss 99 | # print(label) 100 | loss = loss_func(output, y) 101 | # loss = loss_compute(output, y, w_loss) 102 | loss.backward() 103 | loss_all += loss.item() * len(y) 104 | optimizer.step() 105 | scheduler.step(mae_best) 106 | if (epoch+1)%50==0: # len(data)<128 & epoch%50==0: 107 | print(f"Epoch{epoch+1}learning rate{optimizer.param_groups[0]['lr']}" ) 108 | loss_epoch[epoch] = loss_all / len(train_dataset) 109 | 110 | if (epoch+1) % 5 == 0: 111 | print(f'epoch:{epoch+1}, loss:{loss_epoch[epoch]}') 112 | 113 | if (epoch+1) % 10 == 0: 114 | model.eval() 115 | 116 | val_loader = DataLoader(val_dataset, batch_size=500, shuffle=False) 117 | labels, preds = [], [] 118 | for data in val_loader: 119 | label = data.y.numpy() 120 | labels += list(label) 121 | data = data.to('cuda') 122 | pred = model(data).cpu().detach().numpy() 123 | preds += list(pred) 124 | mae = mean_absolute_error(labels, preds) 125 | 126 | test_loader = DataLoader(test_dataset, batch_size=500, shuffle=False) 127 | labels, preds = [], [] 128 | for data in test_loader: 129 | label = data.y.numpy() 130 | labels += list(label) 131 | data = data.to('cuda') 132 | pred = model(data).cpu().detach().numpy() 133 | preds += list(pred) 134 | mae_test = mean_absolute_error(labels, preds) 135 | 136 | print(f'Val dataset, mae loss:', mae) 137 | print(f'Test dataset, mae loss:', mae_test) 138 | if mae < mae_best: 139 | mae_best = mae 140 | test = mae_test 141 | best_epoch = epoch 142 | np.savez(f'prediction/transient_conti/acc-{time}.npz', label=labels, preds = preds) 143 | torch.save(model.state_dict(), f"prediction/transient_conti/model_parameter-{time}.pkl") 144 | print(f'Accuracy has been updated, and new model has been saved!') 145 | print(f'times={time}, epoch:{epoch+1}, until now,the best mae loss on Val dataset:{mae_best},Test dataset:{test}, best epoch:{best_epoch+1}') 146 | 147 | model.train() 148 | 149 | if epoch - best_epoch >= 300: 150 | print('!!Since best_mae is not updated for too long, break for next training!') 151 | break 152 | 153 | 154 | # save loss 155 | np.savez(f'prediction/transient_conti/loss-{time}.npz', loss=loss_epoch) 156 | 157 | 158 | 159 | 160 | --------------------------------------------------------------------------------