OSCR

Independence and coherence in temporal sequence computation across the fronto-parietal network.

Code ↔ Paper

4 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 4 matches
  1. [1] § Methods › Dimensionality reduction ↔ functions/decode_analyze.py, lines 875–881 · score 0.62 · intra trial variance, inter trial variance, decoding, component
  2. [2] § Methods › Twin RNN models ↔ functions/decode_analyze.py, lines 75–92 · score 0.61 · Frequency components, white noise, Hz, domain, transforming, zero
  3. [3] § Methods › Twin RNN models ↔ functions/decode_analyze.py, lines 113–144 · score 0.60 · Gaussian noise, ReLU, alpha, leaky, activation, RNN
  4. [4] § Methods › RNN training ↔ functions/decode_analyze.py, lines 2075–2177 · score 0.53 · stateful RNN, TensorFlow, L2, sequences, batch, models

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 2,414 lines · 122 KB · no license · 4 matches

  1. import numpy as np
  2. import random
  3. import tensorflow as tf
  4. from tensorflow.keras.layers import Input, Dense, Concatenate, RNN
  5. from tensorflow.keras.models import Model
  6. from tensorflow.keras import initializers
  7. from tensorflow.keras.regularizers import l2
  8. from tensorflow.keras.initializers import GlorotUniform
  9. from sklearn.cross_decomposition import CCA, PLSSVD
  10. from sklearn.decomposition import PCA
  11. from sklearn.ensemble import RandomForestClassifier
  12. from RNNcustom_2_fix2 import RNNCustom2Fix2
  13. from RNNcustom_2_fix_2_full_brown import RNNCustom2Fix2full_brown_2
  14. from CustomConstraintWithMax import IEWeightandLim, IEWeightOut
  15. from WInitial_3 import OrthoCustom3
  16. from RNNcustom_2_perturb_noise_prob import (
  17. RNNCustom2FixPerturb_noise_prob,
  18. RNNCustom2FixPerturb_noise_prob_brown,
  19. RNNCustom2FixPerturb_noise_dir_prob_brown,
  20. RNNCustom2FixPerturb_noise_prob_brown_lyapunov,
  21. RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian,
  22. RNNCustom2FixPerturb_noise_prob_synch_jacobian,
  23. RNNCustom2FixPerturb_noise_dir_prob_brown_return_noise,
  24. RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_2,
  25. RNNCustom2FixPerturb_noise_dir_prob_brown_ratio,
  26. RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_output
  27. )
  28. from CCA_SVD import CCA_SVD
  29. from PLS_SVD import PLS_SVD
  30. from OrthoCCA import CCA_ortho
  31. from column_corr import pairwise_corr
  32. from scipy.linalg import null_space
  33. from QRDCell2 import (QRDcell2_flat_2,
  34. Get_norm_ratio,
  35. CumulativeJacobian,
  36. EigOrSingJacobian,
  37. Get_lyap_dir,
  38. Noise_jacob,
  39. QRDcell2_flat_half,
  40. Running_correlation,
  41. DiagonalLayer,
  42. LE_axis_diff,
  43. Jxy_val)
  44. import warnings
  45. class PerturbDecodeAnalyze:
  46. def __init__(self, min_dur, max_dur, dt, dim_method, Dim=100, lin_method="act_avg",nohigh=True):
  47. self.min_dur = min_dur
  48. self.max_dur = max_dur
  49. self.dt = dt
  50. self.dim_method = dim_method
  51. self.fit_method = lin_method
  52. self.Dim = Dim
  53. self.nohigh=nohigh
  54. def build_masks(self, nUnit, nInh, con_prob, seed):
  55. random_matrix = tf.random.uniform([nUnit - nInh, nUnit], minval=0, maxval=1, seed=seed)
  56. # Apply threshold to generate binary values
  57. mask_A_1 = tf.cast(tf.random.uniform([nUnit - nInh, nUnit], minval=0, maxval=1) < con_prob, dtype=tf.int32)
  58. mask_A = tf.concat([mask_A_1, tf.zeros([nInh, nUnit], dtype=tf.int32)], 0)
  59. return mask_A
  60. def column_wise_brown(self, length,rank,exp,freq_scale=1.0):
  61. # shape: (length, rank)
  62. white_noise = np.random.normal(size=(length, rank))
  63. fft_noise = np.fft.fft(white_noise, axis=0)
  64. freqs = np.fft.fftfreq(length) * freq_scale
  65. freqs[0] = freqs[1] # avoid 0
  66. scale_factor = 1.0 / (np.abs(freqs) ** exp) # shape (length,)
  67. # Apply scaling to each column
  68. scaled_fft_noise = fft_noise * scale_factor[:, None]
  69. brown_1d = np.fft.ifft(scaled_fft_noise, axis=0).real
  70. return brown_1d
  71. def column_wise_brown_nohigh(self, length, rank, exp, freq_scale=100.0):
  72. # Generate white noise of shape (length, rank)
  73. white_noise = np.random.normal(size=(length, rank))
  74. # Compute FFT along the time dimension (axis=0)
  75. fft_noise = np.fft.fft(white_noise, axis=0)
  76. # Create frequency bins in Hz: d=1/sampling_rate ensures the correct scaling
  77. freqs = np.fft.fftfreq(length, d=1/freq_scale)
  78. # Avoid division by zero at f=0; assign it a value from the next frequency bin
  79. freqs[0] = freqs[1]
  80. # Compute the scaling factor: 1/|f|^exp for each frequency bin
  81. scale_factor = 1.0 / (np.abs(freqs) ** exp)
  82. # Eliminate all frequency components with period < 1 second (i.e., f > 1 Hz)
  83. scale_factor[np.abs(freqs) > 1] = 0
  84. # Apply the scaling factor column-wise to the FFT noise components
  85. scaled_fft_noise = fft_noise * scale_factor[:, None]
  86. # Transform the scaled data back to the time domain and take the real part
  87. brown_1d = np.fft.ifft(scaled_fft_noise, axis=0).real
  88. return brown_1d
  89. def column_wise_noise(self,length, rank, exp, freq_scale=100.0):
  90. if self.nohigh is True:
  91. noise_weights=self.column_wise_brown_nohigh(length,rank,exp, freq_scale=freq_scale)
  92. else:
  93. noise_weights=self.column_wise_brown(length,rank,exp, freq_scale=freq_scale)
  94. return noise_weights
  95. #noise_weights=tf.convert_to_tensor(column_wise_brown(nUnit,rank,exp=2).T) #(rank, nUnit)
  96. def create_noise_weights(self, length,rank,exp,scale):
  97. noise_weights=self.column_wise_noise(length,rank,exp)
  98. noise_weights=scale * (noise_weights - np.min(noise_weights,axis=0,keepdims=True))/ (np.max(noise_weights,axis=0, keepdims=True) - np.min(noise_weights,axis=0, keepdims=True))-0.5*scale
  99. return noise_weights.T # rank, time
  100. def create_brown_noise_rank(self, time,rank,sample_size,exp,scale):
  101. noise_inputs=self.column_wise_noise(time,rank*sample_size,exp=exp)
  102. noise_inputs = scale * (noise_inputs - np.min(noise_inputs,axis=0,keepdims=True)) / (np.max(noise_inputs,axis=0, keepdims=True) - np.min(noise_inputs,axis=0, keepdims=True))-0.5*scale
  103. noise_inputs=np.array(np.split(noise_inputs, indices_or_sections=sample_size, axis=1))#(samplesize,time,rank)
  104. return noise_inputs #(samplesize,time,rank)
  105. def build_model(self, nUnit,nInh,nInput,con_prob,maxval,ReLUalpha,tau,seed1):
  106. A_mask=self.build_masks(nUnit,nInh, con_prob,seed=seed1)
  107. B_mask=self.build_masks(nUnit,nInh, con_prob,seed=seed1)
  108. visible = Input(shape=(None,nInput))
  109. #vis_noise=GaussianNoiseAdd(stddev=0.01, seed=seed1)(visible)# used to be 0.01*np.sqrt(tau*2)
  110. #hidden1 = SimpleRNN(nUnit,activation='tanh', use_bias=False, batch_size=batch_sz, stateful=False, input_shape=(None, 1), return_sequences=True)(vis_noise)
  111. # the code below incorporated options to train input kernel within RNN layer
  112. hidden1=RNN(RNNCustom2Fix2(nUnit,
  113. output_activation=tf.keras.layers.ReLU(max_value=1000),
  114. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  115. use_bias=False,
  116. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1), # kernel initializer should be random normal
  117. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1 , nUnit=nUnit, nInh=nInh, conProb=con_prob),
  118. recurrent_constraint=IEWeightandLim(nInh=nInh,A_mask=A_mask,B_mask=B_mask,maxval=maxval),
  119. kernel_trainable=True,
  120. seed=seed1,
  121. tau=tau,
  122. noisesd=0.08), # used to be 0.05*np.sqrt(tau*2)
  123. stateful=False,
  124. input_shape=(None, nInput),
  125. return_sequences=True,
  126. activity_regularizer=l2(0.01)# used to be 0.0001, 0.000001
  127. #recurrent_regularizer=l2(0.000001)
  128. )(visible)
  129. # hidden2 = Dense(10, activation='relu')(hidden1)
  130. output_A = Dense(2, activation='tanh',kernel_initializer=GlorotUniform(seed=seed1), kernel_constraint=IEWeightOut(nInh=nInh))(hidden1[0])
  131. output_B = Dense(2, activation='tanh',kernel_initializer=GlorotUniform(seed=seed1), kernel_constraint=IEWeightOut(nInh=nInh))(hidden1[1])
  132. output=Concatenate(axis=2)([output_A,output_B])
  133. model = Model(inputs=visible, outputs=output)
  134. return model
  135. def build_model_perturb_noise_prob(self, nUnit, nInh, nInput, con_prob, maxval, ReLUalpha,
  136. pert_ind, pert_which, seed1, pert_noisesd, tau):
  137. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  138. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  139. visible = Input(shape=(None, nInput))
  140. # Optionally add noise: vis_noise = GaussianNoiseAdd(stddev=0.01, seed=seed1)(visible)
  141. hidden1 = RNN(
  142. RNNCustom2FixPerturb_noise_prob(
  143. nUnit,
  144. output_activation=tf.keras.layers.ReLU(max_value=1000),
  145. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  146. use_bias=False,
  147. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  148. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  149. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  150. kernel_trainable=True,
  151. seed=seed1,
  152. tau=tau,
  153. noisesd=0.08,
  154. perturb_ind=pert_ind,
  155. pert_which=pert_which,
  156. pert_noisesd=pert_noisesd
  157. ),
  158. stateful=False,
  159. input_shape=(None, nInput),
  160. return_sequences=True,
  161. activity_regularizer=l2(0.01)
  162. )(visible)
  163. output_A = Dense(2, activation='tanh',
  164. kernel_initializer=GlorotUniform(seed=seed1),
  165. kernel_constraint=IEWeightOut(nInh=nInh))(hidden1[0])
  166. output_B = Dense(2, activation='tanh',
  167. kernel_initializer=GlorotUniform(seed=seed1),
  168. kernel_constraint=IEWeightOut(nInh=nInh))(hidden1[1])
  169. output = Concatenate(axis=2)([output_A, output_B])
  170. model = Model(inputs=visible, outputs=output)
  171. return model
  172. def build_model_brown(self,nUnit,nInh,nInput,con_prob,maxval,ReLUalpha,seed1,tau,rank,exp=1,noise_weights=None):
  173. A_mask=self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  174. B_mask=self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  175. visible = Input(shape=(None,nInput+rank))
  176. #vis_noise=GaussianNoiseAdd(stddev=0.01, seed=seed1)(visible)# used to be 0.01*np.sqrt(tau*2)
  177. #hidden1 = SimpleRNN(nUnit,activation='tanh', use_bias=False, batch_size=batch_sz, stateful=False, input_shape=(None, 1), return_sequences=True)(vis_noise)
  178. if noise_weights is None:
  179. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  180. print('Random noise weight created')
  181. else:
  182. self.noise_weights=noise_weights.copy()
  183. # Create your custom RNN cell
  184. rnn_cell = RNNCustom2Fix2full_brown_2(
  185. nUnit,
  186. output_activation=tf.keras.layers.ReLU(max_value=1000),
  187. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  188. use_bias=False,
  189. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  190. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  191. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  192. kernel_trainable=True,
  193. seed=seed1,
  194. tau=tau,
  195. noisesd=0.08,
  196. noise_weights=tf.convert_to_tensor(self.noise_weights),
  197. )
  198. # Create the RNN layer with your custom cell and set it to stateless
  199. rnn_layer = RNN(
  200. rnn_cell,
  201. stateful=False, # now stateless
  202. return_sequences=True,
  203. activity_regularizer=l2(0.1)
  204. )
  205. # Call the RNN layer; initial states will be automatically set to zeros
  206. hidden_outputs = rnn_layer(visible)
  207. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  208. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  209. # Define the output layers
  210. output_A = Dense(2, activation='tanh',
  211. kernel_initializer=GlorotUniform(seed=seed1),
  212. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  213. output_B = Dense(2, activation='tanh',
  214. kernel_initializer=GlorotUniform(seed=seed1),
  215. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  216. # Concatenate the outputs
  217. output = Concatenate(axis=-1)([output_A, output_B])
  218. # Define the model with only the visible input
  219. model = Model(inputs=visible, outputs=output)
  220. return model
  221. def build_model_perturb_noise_prob_brown(self, nUnit, nInh, nInput, con_prob, maxval, ReLUalpha,
  222. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=1,noise_weights=None):
  223. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  224. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  225. visible = Input(shape=(None,nInput+rank))
  226. if noise_weights is None:
  227. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  228. print('Random noise weight created')
  229. else:
  230. self.noise_weights=noise_weights.copy()
  231. rnn_cell = RNNCustom2FixPerturb_noise_prob_brown(
  232. nUnit,
  233. output_activation=tf.keras.layers.ReLU(max_value=1000),
  234. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  235. use_bias=False,
  236. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  237. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  238. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  239. kernel_trainable=True,
  240. seed=seed1,
  241. tau=tau,
  242. noisesd=0.08,
  243. perturb_ind=pert_ind,
  244. pert_which=pert_which,
  245. pert_noisesd=pert_noisesd,
  246. noise_weights=tf.convert_to_tensor(self.noise_weights),
  247. )
  248. # Create the RNN layer with your custom cell and set it to stateless
  249. rnn_layer = RNN(
  250. rnn_cell,
  251. stateful=False, # now stateless
  252. return_sequences=True,
  253. activity_regularizer=l2(0.1)
  254. )
  255. # Call the RNN layer; initial states will be automatically set to zeros
  256. hidden_outputs = rnn_layer(visible)
  257. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  258. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  259. # Define the output layers
  260. output_A = Dense(2, activation='tanh',
  261. kernel_initializer=GlorotUniform(seed=seed1),
  262. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  263. output_B = Dense(2, activation='tanh',
  264. kernel_initializer=GlorotUniform(seed=seed1),
  265. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  266. # Concatenate the outputs
  267. output = Concatenate(axis=-1)([output_A, output_B])
  268. # Define the model with only the visible input
  269. model = Model(inputs=visible, outputs=output)
  270. return model
  271. def build_model_perturb_noise_prob_brown_dir(self, nUnit, nInh, nInput, con_prob, maxval, ReLUalpha,
  272. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=1,noise_weights=None,
  273. noise_vec=None, sync_noise=True):
  274. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  275. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  276. visible = Input(shape=(None,nInput+rank))
  277. if noise_weights is None:
  278. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  279. print('Random noise weight created')
  280. else:
  281. self.noise_weights=noise_weights.copy()
  282. rnn_cell = RNNCustom2FixPerturb_noise_dir_prob_brown(
  283. nUnit,
  284. output_activation=tf.keras.layers.ReLU(max_value=1000),
  285. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  286. use_bias=False,
  287. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  288. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  289. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  290. kernel_trainable=True,
  291. seed=seed1,
  292. tau=tau,
  293. noisesd=0.08,
  294. perturb_ind=pert_ind,
  295. pert_which=pert_which,
  296. pert_noisesd=pert_noisesd,
  297. noise_weights=tf.convert_to_tensor(self.noise_weights),
  298. noise_vec=noise_vec,
  299. sync_noise=sync_noise,
  300. )
  301. # Create the RNN layer with your custom cell and set it to stateless
  302. rnn_layer = RNN(
  303. rnn_cell,
  304. stateful=False, # now stateless
  305. return_sequences=True,
  306. activity_regularizer=l2(0.1)
  307. )
  308. # Call the RNN layer; initial states will be automatically set to zeros
  309. hidden_outputs = rnn_layer(visible)
  310. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  311. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  312. # Define the output layers
  313. output_A = Dense(2, activation='tanh',
  314. kernel_initializer=GlorotUniform(seed=seed1),
  315. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  316. output_B = Dense(2, activation='tanh',
  317. kernel_initializer=GlorotUniform(seed=seed1),
  318. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  319. # Concatenate the outputs
  320. output = Concatenate(axis=-1)([output_A, output_B])
  321. # Define the model with only the visible input
  322. model = Model(inputs=visible, outputs=output)
  323. return model
  324. def build_model_perturb_noise_prob_brown_dir_ratio(self, nUnit, nInh, nInput, con_prob, maxval, ReLUalpha,
  325. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=0.5,noise_weights=None,
  326. pert_noise_mat=None, sync_noise=True):
  327. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  328. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  329. visible = Input(shape=(None,nInput+rank+pert_noise_mat[0].shape[1]))
  330. if noise_weights is None:
  331. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  332. print('Random noise weight created')
  333. else:
  334. self.noise_weights=noise_weights.copy()
  335. rnn_cell = RNNCustom2FixPerturb_noise_dir_prob_brown_ratio(
  336. nUnit,
  337. output_activation=tf.keras.layers.ReLU(max_value=1000),
  338. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  339. use_bias=False,
  340. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  341. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  342. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  343. kernel_trainable=True,
  344. seed=seed1,
  345. tau=tau,
  346. noisesd=0.08,
  347. perturb_ind=pert_ind,
  348. pert_which=pert_which,
  349. pert_noisesd=pert_noisesd,
  350. noise_weights=tf.convert_to_tensor(self.noise_weights),
  351. pert_noise_mat=pert_noise_mat,
  352. sync_noise=sync_noise,
  353. )
  354. # Create the RNN layer with your custom cell and set it to stateless
  355. rnn_layer = RNN(
  356. rnn_cell,
  357. stateful=False, # now stateless
  358. return_sequences=True,
  359. activity_regularizer=l2(0.1)
  360. )
  361. # Call the RNN layer; initial states will be automatically set to zeros
  362. hidden_outputs = rnn_layer(visible)
  363. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  364. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  365. # Define the output layers
  366. output_A = Dense(2, activation='tanh',
  367. kernel_initializer=GlorotUniform(seed=seed1),
  368. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  369. output_B = Dense(2, activation='tanh',
  370. kernel_initializer=GlorotUniform(seed=seed1),
  371. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  372. # Concatenate the outputs
  373. output = Concatenate(axis=-1)([output_A, output_B])
  374. # Define the model with only the visible input
  375. model = Model(inputs=visible, outputs=output)
  376. return model
  377. def makeInput(self, x, In_ons, pert_ind):
  378. In_ons2 = []
  379. for i in range(np.shape(In_ons)[0]):
  380. # Find the first index where the condition is met
  381. index = np.argmax(In_ons[i, :] >= pert_ind[i, 0]) if np.any(In_ons[i, :] >= pert_ind[i, 0]) else None
  382. if index is not None:
  383. x[i, In_ons[i, index] - 1:, :] = np.random.normal(
  384. loc=0.0, scale=0.01, size=x[i, In_ons[i, index] - 1:, :].shape)
  385. In_ons2.append(In_ons[i, :index - 1])
  386. else:
  387. In_ons2.append(In_ons[i, :])
  388. return x, In_ons2
  389. def avgAct(self, activities, In_ons):
  390. dura = [self.min_dur, self.max_dur]
  391. dur0 = In_ons[0, 1] - In_ons[0, 0]
  392. ind1 = np.argmin(np.abs(dur0 - np.array(dura)))
  393. act_avg = np.zeros((self.min_dur + self.max_dur, activities.shape[2]))
  394. kk = 0
  395. for i in range(activities.shape[0]):
  396. In_time = In_ons[i, 1 + ((ind1 + i + 1) % 2) : -2 : 2]
  397. for j in In_time:
  398. act_avg += np.squeeze(activities[i, j : j + self.min_dur + self.max_dur, :])
  399. kk += 1
  400. act_avg /= kk
  401. return act_avg
  402. def avgAct2(self, activities, In_ons):
  403. avg_dur = (self.min_dur + self.max_dur) / 2
  404. dur0 = In_ons[0, 1] - In_ons[0, 0]
  405. ind1 = np.argmin(np.abs(dur0 - np.array([self.min_dur, self.max_dur])))
  406. act_avg = np.zeros((self.min_dur + self.max_dur, activities.shape[2]))
  407. kk = 0
  408. for i in range(activities.shape[0]):
  409. for j in range(1,In_ons.shape[1] - 1):
  410. if (In_ons[i, j + 1] - In_ons[i, j] <= avg_dur and
  411. In_ons[i, j] + self.min_dur + self.max_dur <= activities.shape[1]):
  412. act_avg += np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur + self.max_dur, :])
  413. kk += 1
  414. act_avg /= kk
  415. return act_avg
  416. def avgAct2_stack(self, activities, In_ons):
  417. """
  418. Extracts and stacks all valid activity segments of length (min_dur+max_dur)
  419. for each trial & onset in `In_ons`. Returns an array of shape
  420. (n_segments, min_dur+max_dur, activities.shape[2])
  421. """
  422. combined = self.min_dur + self.max_dur
  423. segments = []
  424. n_trials, n_onsets = In_ons.shape
  425. for i in range(n_trials):
  426. for j in range(1, n_onsets - 1):
  427. start = In_ons[i, j]
  428. end = start + combined
  429. # check that this segment fits within the time-axis
  430. if (In_ons[i, j + 1] - start <= (combined / 2) and end <= activities.shape[1]):
  431. seg = activities[i, start:end, :] # shape = (combined, n_channels)
  432. segments.append(seg)
  433. if len(segments) == 0:
  434. # no valid segments found → return empty array
  435. return np.zeros((0, combined, activities.shape[2]))
  436. return np.stack(segments, axis=0)
  437. def concatAct(self, activities, In_ons):
  438. segments = []
  439. for i in range(activities.shape[0]):
  440. for j in range(1, In_ons.shape[1] - 1):
  441. if (In_ons[i, j + 1] - In_ons[i, j] <= (self.min_dur + self.max_dur) / 2 and
  442. In_ons[i, j] + self.min_dur + self.max_dur <= activities.shape[1]):
  443. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur + self.max_dur, :])
  444. segments.append(segment)
  445. if segments:
  446. act_concat = np.concatenate(segments, axis=0) # Concatenate along the time axis
  447. else:
  448. act_concat = np.zeros((0, activities.shape[2])) # Return an empty array if no segments
  449. return act_concat
  450. def concatAct_sliced(self, activities, In_ons):
  451. # batch, time, units
  452. final_act_concat = [] # List to store act_concat for each slice
  453. for i in range(activities.shape[0]): # Iterate over the first dimension
  454. segments = []
  455. for j in range(1, In_ons.shape[1] - 1):
  456. if (In_ons[i, j + 1] - In_ons[i, j] <= (self.min_dur + self.max_dur) / 2 and
  457. In_ons[i, j] + self.min_dur + self.max_dur <= activities.shape[1]):
  458. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur + self.max_dur, :])
  459. segments.append(segment)
  460. if segments:
  461. act_concat = np.concatenate(segments, axis=0) # Concatenate segments within this slice
  462. else:
  463. act_concat = np.zeros((0, activities.shape[2])) # Empty array if no valid segments
  464. final_act_concat.append(act_concat) # Store each act_concat in the list
  465. return np.array(final_act_concat) # Returns a list where each element corresponds to a slice
  466. def concatAct_with_class(self, activities, In_ons, class_per_trial):
  467. segments = []
  468. class_vec=[]
  469. avg_dur=0.5*(self.min_dur+self.max_dur)
  470. t_per_class=int((self.min_dur+self.max_dur)/class_per_trial)
  471. min_dur_class=int(class_per_trial*(self.min_dur)/(self.min_dur+self.max_dur))
  472. for i in range(activities.shape[0]):
  473. for j in range(1, In_ons.shape[1] - 1):
  474. if In_ons[i, j] + self.max_dur <= activities.shape[1]:
  475. if In_ons[i, j + 1] - In_ons[i, j] <= avg_dur:
  476. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur, :])
  477. class_sub=np.repeat(np.arange(min_dur_class),t_per_class,axis=0)
  478. else:
  479. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.max_dur, :])
  480. class_sub=np.repeat(np.arange(min_dur_class,class_per_trial),t_per_class,axis=0)
  481. segments.append(segment)
  482. class_vec.append(class_sub)
  483. if segments:
  484. act_concat = np.concatenate(segments, axis=0) # Concatenate along the time axis
  485. class_vec = np.concatenate(class_vec, axis=0)
  486. else:
  487. act_concat = np.zeros((0, activities.shape[2])) # Return an empty array if no segments
  488. return act_concat, class_vec
  489. def concatAct_with_class_min_max(self, activities, In_ons, class_per_trial):
  490. segments_min = []
  491. segments_max = []
  492. class_min=[]
  493. class_max=[]
  494. avg_dur=0.5*(self.min_dur+self.max_dur)
  495. t_per_class=int((self.min_dur+self.max_dur)/class_per_trial)
  496. min_dur_class=int(class_per_trial*(self.min_dur)/(self.min_dur+self.max_dur))
  497. for i in range(activities.shape[0]):
  498. for j in range(1, In_ons.shape[1] - 1):
  499. if In_ons[i, j] + self.max_dur <= activities.shape[1]:
  500. if In_ons[i, j + 1] - In_ons[i, j] <= avg_dur:
  501. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur, :])
  502. segments_min.append(segment)
  503. class_sub=np.repeat(np.arange(min_dur_class),t_per_class,axis=0)
  504. class_min.append(class_sub)
  505. else:
  506. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.max_dur, :])
  507. segments_max.append(segment)
  508. class_sub=np.repeat(np.arange(min_dur_class,class_per_trial),t_per_class,axis=0)
  509. class_max.append(class_sub)
  510. act_cat_min = np.stack(segments_min, axis=-1) # Concatenate along the time axis
  511. act_cat_max = np.stack(segments_max, axis=-1) # Concatenate along the time axis
  512. class_minvec = np.stack(class_min, axis=-1)
  513. class_maxvec = np.stack(class_max, axis=-1)
  514. return act_cat_min, act_cat_max, class_minvec, class_maxvec
  515. def concatAct_with_in_ons_new(self, activities, in_ons_new, class_per_trial):
  516. segments_min = []
  517. segments_max = []
  518. class_min=[]
  519. class_max=[]
  520. avg_dur=0.5*(self.min_dur+self.max_dur)
  521. t_per_class=int((self.min_dur+self.max_dur)/class_per_trial)
  522. min_dur_class=int(class_per_trial*(self.min_dur)/(self.min_dur+self.max_dur))
  523. for i in range(activities.shape[0]):
  524. for j in range(1, in_ons_new[0][i].shape[0] - 1):
  525. if in_ons_new[0][i][j] + self.min_dur<activities.shape[1]:
  526. seg_min=np.squeeze(activities[i, in_ons_new[0][i][j] : in_ons_new[0][i][j] + self.min_dur, :])
  527. segments_min.append(seg_min)
  528. class_sub=np.repeat(np.arange(min_dur_class),t_per_class,axis=0)
  529. class_min.append(class_sub)
  530. for j in range(1, in_ons_new[1][i].shape[0] - 1):
  531. if in_ons_new[1][i][j] + self.max_dur< activities.shape[1]:
  532. seg_max=np.squeeze(activities[i, in_ons_new[1][i][j] : in_ons_new[1][i][j] + self.max_dur, :])
  533. segments_max.append(seg_max)
  534. class_sub=np.repeat(np.arange(min_dur_class,class_per_trial),t_per_class,axis=0)
  535. class_max.append(class_sub)
  536. act_cat_min = np.stack(segments_min, axis=-1) # Concatenate along the time axis
  537. act_cat_max = np.stack(segments_max, axis=-1) # Concatenate along the time axis
  538. class_minvec = np.stack(class_min, axis=-1)
  539. class_maxvec = np.stack(class_max, axis=-1)
  540. return act_cat_min, act_cat_max, class_minvec, class_maxvec
  541. def concatAct_sliced(self, activities, In_ons):
  542. # batch, time, units
  543. final_act_concat = [] # List to store act_concat for each slice
  544. for i in range(activities.shape[0]): # Iterate over the first dimension
  545. segments = []
  546. for j in range(1, In_ons.shape[1] - 1):
  547. if (In_ons[i, j + 1] - In_ons[i, j] <= (self.min_dur + self.max_dur) / 2 and
  548. In_ons[i, j] + self.min_dur + self.max_dur <= activities.shape[1]):
  549. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur + self.max_dur, :])
  550. segments.append(segment)
  551. if segments:
  552. act_concat = np.concatenate(segments, axis=0) # Concatenate segments within this slice
  553. else:
  554. act_concat = np.zeros((0, activities.shape[2])) # Empty array if no valid segments
  555. final_act_concat.append(act_concat) # Store each act_concat in the list
  556. return np.array(final_act_concat) # Returns a list where each element corresponds to a slice
  557. def concatAct_list(self, activities, In_ons):
  558. segments = []
  559. for i in range(activities.shape[0]):
  560. for j in range(1, In_ons.shape[1] - 1):
  561. if (In_ons[i, j + 1] - In_ons[i, j] <= (self.min_dur + self.max_dur) / 2 and
  562. In_ons[i, j] + self.min_dur + self.max_dur <= activities.shape[1]):
  563. segment = np.squeeze(activities[i, In_ons[i, j] : In_ons[i, j] + self.min_dur + self.max_dur, :])
  564. segments.append(segment)
  565. return np.array(segments)
  566. def make_in_ons(self, x):
  567. In_ons=[]
  568. for k in range(x.shape[-1]):
  569. In_ons_1=[]
  570. for i in range(x.shape[0]):
  571. in_ons_sub=np.where((x[i][1:,k]>0.5) & (x[i][:-1,k] <0.5))[0] + 1
  572. In_ons_1.append(in_ons_sub)
  573. In_ons.append(In_ons_1)
  574. return In_ons
  575. def sort_and_scale2(self, act_avg_A,start,stop):
  576. # act_avg_A is matrix with size (time, nCells)
  577. # this function first sort cells accorcding to the time of maximum activity from start to stop
  578. # then this function scale the value so that the maximum is 1.
  579. act_avg_A=np.array(act_avg_A)
  580. maxtime=np.argmax(act_avg_A[start:stop+1,:],0)
  581. maxtime_sort=np.argsort(maxtime)
  582. act_avg_A=act_avg_A[:,maxtime_sort]
  583. act_avg_new=act_avg_A/np.maximum(np.max(act_avg_A[start:stop+1,:],0),1e-10) # scale the matrix to set maximum to 1
  584. return act_avg_new, maxtime_sort
  585. def makeInOut_sameint(self, sample_size, trial_num, inputdur, nInput, interval):
  586. noise_range = 0
  587. max_dur_max = np.ceil((1 + noise_range) * self.max_dur)
  588. min_dur_max = np.ceil((1 + noise_range) * self.min_dur)
  589. total_time_orig = int(min_dur_max * np.floor(trial_num / 2) +
  590. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  591. trial_num = int(2 * np.ceil(trial_num / 2))
  592. total_time = int(min_dur_max * np.floor(trial_num / 2) +
  593. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  594. x = np.zeros((sample_size, total_time, nInput))
  595. y = -0.5 * np.ones((sample_size, total_time, 2))
  596. In_ons = np.zeros((sample_size, trial_num), dtype=np.int64)
  597. for i in range(sample_size):
  598. vec = total_time
  599. for j in range(trial_num):
  600. vecbf = vec
  601. if j % 2 == interval:
  602. vec -= self.min_dur + random.randint(-int(self.min_dur * noise_range),
  603. int(self.min_dur * noise_range))
  604. else:
  605. vec -= self.max_dur + random.randint(-int(self.max_dur * noise_range),
  606. int(self.max_dur * noise_range))
  607. In_ons[i, -j - 1] = vec
  608. in_start = vec
  609. Dur = vecbf - in_start
  610. x[i, vec : vec + inputdur, :] = 1
  611. y[i, in_start : vecbf, 0] = np.power(np.linspace(0, 1, num=Dur), 4) - 0.5
  612. y[i, in_start : vecbf, 1] = np.arange(-0.5,-0.5+(Dur/self.max_dur)-1e-10,1/self.max_dur) # aboslute timing 1
  613. x += np.random.normal(loc=0.0, scale=0.01, size=x.shape)
  614. y = np.tile(y, (1, 1, 2))
  615. x = x[:, :total_time_orig, :]
  616. y = y[:, :total_time_orig, :]
  617. return x, y, In_ons
  618. def makeInOut_sameint_brown(self,sample_size,trial_num,inputdur,nInput,interval,brown_scale, rank, exp, noise_range=0):
  619. max_dur_max = np.ceil((1 + noise_range) * self.max_dur)
  620. min_dur_max = np.ceil((1 + noise_range) * self.min_dur)
  621. total_time_orig = int(min_dur_max * np.floor(trial_num / 2) +
  622. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  623. trial_num = int(2 * np.ceil(trial_num / 2))
  624. total_time = int(min_dur_max * np.floor(trial_num / 2) +
  625. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  626. x = np.zeros((sample_size, total_time, nInput))
  627. y = -0.5 * np.ones((sample_size, total_time, 2))
  628. noise_inputs=self.create_brown_noise_rank(total_time,rank,sample_size,exp,brown_scale) #(samplesize,time,rank)
  629. In_ons = np.zeros((sample_size, trial_num), dtype=np.int64)
  630. for i in range(sample_size):
  631. vec = total_time
  632. for j in range(trial_num):
  633. vecbf = vec
  634. if j % 2 == interval:
  635. vec -= self.min_dur + random.randint(-int(self.min_dur * noise_range),
  636. int(self.min_dur * noise_range))
  637. else:
  638. vec -= self.max_dur + random.randint(-int(self.max_dur * noise_range),
  639. int(self.max_dur * noise_range))
  640. In_ons[i, -j - 1] = vec
  641. in_start = vec
  642. Dur = vecbf - in_start
  643. x[i, vec : vec + inputdur, :] = 1
  644. y[i, in_start : vecbf, 0] = np.power(np.linspace(0, 1, num=Dur), 4) - 0.5
  645. y[i, in_start : vecbf, 1] = np.arange(-0.5,-0.5+(Dur/self.max_dur)-1e-10,1/self.max_dur) # aboslute timing 1
  646. x += np.random.normal(loc=0.0, scale=0.01, size=x.shape)
  647. x=np.concatenate((x,noise_inputs),axis=2)
  648. y = np.tile(y, (1, 1, 2))
  649. x = x[:, :total_time_orig, :]
  650. y = y[:, :total_time_orig, :]
  651. return x, y, In_ons
  652. def makeInOut_sameint_brown_stateful(self,sample_size,trial_num,inputdur,
  653. nInput,interval,brown_scale,
  654. rank, exp,
  655. batch_num, noise_range=0):
  656. max_dur_max = np.ceil((1 + noise_range) * self.max_dur)
  657. min_dur_max = np.ceil((1 + noise_range) * self.min_dur)
  658. total_time_orig = int(min_dur_max * np.floor(trial_num / 2) +
  659. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  660. trial_num = int(2 * np.ceil(trial_num / 2))
  661. total_time = int(min_dur_max * np.floor(trial_num / 2) +
  662. max_dur_max * np.ceil(trial_num / 2) + 300 / self.dt)
  663. total_time=int(np.ceil(total_time/batch_num)*batch_num)
  664. x = np.zeros((sample_size, total_time, nInput))
  665. Wbin=np.zeros_like(x)
  666. y = -0.5 * np.ones((sample_size, total_time, 2))
  667. noise_inputs=self.create_brown_noise_rank(total_time,rank,sample_size,exp,brown_scale) #(samplesize,time,rank)
  668. In_ons = np.zeros((sample_size, trial_num), dtype=np.int64)
  669. for i in range(sample_size):
  670. vec = total_time
  671. for j in range(trial_num):
  672. vecbf = vec
  673. if j % 2 == interval:
  674. vec -= self.min_dur + random.randint(-int(self.min_dur * noise_range),
  675. int(self.min_dur * noise_range))
  676. else:
  677. vec -= self.max_dur + random.randint(-int(self.max_dur * noise_range),
  678. int(self.max_dur * noise_range))
  679. In_ons[i, -j - 1] = vec
  680. in_start = vec
  681. Dur = vecbf - in_start
  682. x[i, vec : vec + inputdur, :] = 1
  683. y[i, in_start : vecbf, 0] = np.power(np.linspace(0, 1, num=Dur), 4) - 0.5
  684. y[i, in_start : vecbf, 1] = np.arange(-0.5,-0.5+(Dur/self.max_dur)-1e-10,1/self.max_dur) # aboslute timing 1
  685. Wbin[i,In_ons[i,1]:]=1
  686. x += np.random.normal(loc=0.0, scale=0.01, size=x.shape)
  687. x=np.concatenate((x,noise_inputs),axis=2)
  688. y = np.tile(y, (1, 1, 2))
  689. xsplit=np.split(x,batch_num,axis=1)
  690. ysplit=np.split(y,batch_num,axis=1)
  691. Wbinsplit=np.split(Wbin,batch_num,axis=1)
  692. # x_reconstructed = np.concatenate(xsplit, axis=1) # undo the split
  693. return xsplit, ysplit, In_ons, Wbinsplit #(Batch_num, sample_size, time, noutput)
  694. def z_score_with_zero_handling(self, A, dim=0):
  695. mean_A = np.mean(A, axis=dim)
  696. std_A = np.std(A, axis=dim)
  697. std_A[std_A == 0] = 1
  698. return (A - mean_A) / std_A
  699. def make_pertind(self, In_ons, trial1, ind1):
  700. addvec = np.array([int(ind1 / self.dt)])
  701. pert_ind = In_ons[:, [trial1]] + addvec
  702. return pert_ind
  703. def makeit2d(self, actpart_A):
  704. a, b, c = np.shape(actpart_A)
  705. mat = np.zeros((a * c, b))
  706. for i in range(c):
  707. mat[a * i : a * (i + 1), :] = actpart_A[:, :, i]
  708. return mat
  709. def Act_2dsort(self, activities, In_ons):
  710. dura = [self.min_dur, self.max_dur]
  711. dur0 = In_ons[0, 1] - In_ons[0, 0]
  712. ind1 = np.argmin(np.abs(dur0 - np.array(dura)))
  713. act_avg = np.zeros((self.min_dur + self.max_dur, activities.shape[2]))
  714. for i in range(activities.shape[0]):
  715. In_time = In_ons[i, 1 + ((ind1 + i + 1) % 2) : -2 : 2]
  716. for j in In_time:
  717. addmat = np.squeeze(activities[i, j : j + self.min_dur + self.max_dur, :])
  718. act_avg = np.concatenate((act_avg, addmat), axis=0)
  719. act_avg = act_avg[self.min_dur + self.max_dur :, :]
  720. return act_avg
  721. def make_classying_classes(self, act_stack_A, act_stack_B, Class_per_sec):
  722. classleng = int(1000 / (self.dt * Class_per_sec))
  723. class_per_trial = int((self.min_dur + self.max_dur) / classleng)
  724. class_A = np.arange(0, class_per_trial)
  725. class_A = np.repeat(class_A, classleng)
  726. trial_rep_A = int(act_stack_A.shape[0] / (self.min_dur + self.max_dur))
  727. class_A_train = np.tile(class_A, trial_rep_A)
  728. trial_rep_B = int(act_stack_B.shape[0] / (self.min_dur + self.max_dur))
  729. class_B_train = np.tile(class_A, trial_rep_B)
  730. return class_A_train, class_B_train
  731. def make_classying_classes_2(self, size_A, size_B, Class_per_sec):
  732. classleng = int(1000 / (self.dt * Class_per_sec))
  733. class_per_trial = int((self.min_dur + self.max_dur) / classleng)
  734. class_A = np.arange(0, class_per_trial)
  735. class_A = np.repeat(class_A, classleng)
  736. trial_rep_A = int(size_A / (self.min_dur + self.max_dur))
  737. class_A_train = np.tile(class_A, trial_rep_A)
  738. trial_rep_B = int(size_B / (self.min_dur + self.max_dur))
  739. class_B_train = np.tile(class_A, trial_rep_B)
  740. return class_A_train, class_B_train
  741. def remove_inactive(self,act_A,act_B):
  742. self.act_log_A=np.sum(np.power(act_A,2),0)>0
  743. self.act_log_B=np.sum(np.power(act_B,2),0)>0
  744. return act_A[:,self.act_log_A], act_B[:,self.act_log_B]
  745. def remove_inactive_transform(self, act_A, act_B, dim=1):
  746. # Convert boolean masks to indices
  747. indices_A = np.where(self.act_log_A)[0]
  748. indices_B = np.where(self.act_log_B)[0]
  749. return np.take(act_A, indices_A, axis=dim), np.take(act_B,indices_B,axis=dim)
  750. def remove_inactive_1(self,act_A):
  751. self.act_log_C=np.sum(np.power(act_A,2),0)>0
  752. return act_A[:,self.act_log_C]
  753. def remove_inactive_transform_1(self, act_A, dim=1):
  754. # Convert boolean masks to indices
  755. indices_C = np.where(self.act_log_C)[0]
  756. return np.take(act_A, indices_C, axis=dim)
  757. def trial_avg(self,stack_act):
  758. # calculates average activity in a trial for each column
  759. stack_reshape=stack_act.reshape(-1,(self.max_dur+self.min_dur),np.shape(stack_act)[1])
  760. stack_avg=np.mean(stack_reshape,axis=0)
  761. return stack_avg
  762. def intra_inter_var(self,stack_act):
  763. # calculates intra trial variance/inter trial variance
  764. stack_reshape=stack_act.reshape(-1,(self.max_dur+self.min_dur),np.shape(stack_act)[1])# trial, time, components
  765. inter_var=np.sum(np.std(stack_reshape,axis=0),0)#-> time, components-> components
  766. intra_var=np.sum(np.std(stack_reshape,axis=1),0)#-> trial, components-> components
  767. return intra_var/inter_var # (components,)
  768. def reduce_dimension_pre(self, act_avg_A, act_avg_B, act_stack_A, act_stack_B, methodname='pca', Dim=100):
  769. self.Dim_pre=Dim
  770. self.method_pre=methodname.lower()
  771. if methodname.lower() == "pca":
  772. # Create PCA objects
  773. self.method_A_pre = PCA()
  774. self.method_B_pre = PCA()
  775. if self.fit_method.lower() == "act_avg":
  776. # Fit on averaged data and transform both sets
  777. proj_A_train_avg = self.method_A_pre.fit_transform(act_avg_A)[:, :Dim]
  778. proj_B_train_avg = self.method_B_pre.fit_transform(act_avg_B)[:, :Dim]
  779. proj_A_train = self.method_A_pre.transform(act_stack_A)[:, :Dim]
  780. proj_B_train = self.method_B_pre.transform(act_stack_B)[:, :Dim]
  781. elif self.fit_method.lower() == "act_stack":
  782. # Fit on stacked data and transform both sets
  783. proj_A_train = self.method_A_pre.fit_transform(act_stack_A)[:, :Dim]
  784. proj_B_train = self.method_B_pre.fit_transform(act_stack_B)[:, :Dim]
  785. proj_A_train_avg = self.method_A_pre.transform(act_avg_A)[:, :Dim]
  786. proj_B_train_avg = self.method_B_pre.transform(act_avg_B)[:, :Dim]
  787. else:
  788. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  789. return (proj_A_train, proj_B_train, proj_A_train_avg, proj_B_train_avg)
  790. elif methodname.lower() == "cca":
  791. if self.fit_method.lower() == "act_avg":
  792. fit_A, fit_B = act_avg_A, act_avg_B
  793. elif self.fit_method.lower() == "act_stack":
  794. fit_A, fit_B = act_stack_A, act_stack_B
  795. else:
  796. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  797. n_comp = min(fit_A.shape[1], fit_B.shape[1])
  798. self.method_cca_pre = CCA_SVD(n_components=n_comp)
  799. if self.fit_method.lower() == "act_avg":
  800. proj_A_train_avg, proj_B_train_avg = self.method_cca_pre.fit_transform(act_avg_A, act_avg_B)
  801. proj_A_train, proj_B_train = self.method_cca_pre.transform(act_stack_A, act_stack_B)
  802. else:
  803. proj_A_train, proj_B_train = self.method_cca_pre.fit_transform(act_stack_A, act_stack_B)
  804. proj_A_train_avg, proj_B_train_avg = self.method_cca_pre.transform(act_avg_A, act_avg_B)
  805. return proj_A_train[:, :Dim], proj_B_train[:, :Dim], proj_A_train_avg[:, :Dim], proj_B_train_avg[:, :Dim]
  806. elif methodname.lower() == "cca_ortho":
  807. if self.fit_method.lower() == "act_avg":
  808. fit_A, fit_B = act_avg_A, act_avg_B
  809. elif self.fit_method.lower() == "act_stack":
  810. fit_A, fit_B = act_stack_A, act_stack_B
  811. else:
  812. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  813. n_comp = min(fit_A.shape[1], fit_B.shape[1])
  814. self.method_cca_pre = CCA_ortho(n_components=n_comp)
  815. if self.fit_method.lower() == "act_avg":
  816. proj_A_train_avg, proj_B_train_avg = self.method_cca_pre.fit_transform(act_avg_A, act_avg_B)
  817. proj_A_train, proj_B_train = self.method_cca_pre.transform(act_stack_A, act_stack_B)
  818. else:
  819. proj_A_train, proj_B_train = self.method_cca_pre.fit_transform(act_stack_A, act_stack_B)
  820. proj_A_train_avg, proj_B_train_avg = self.method_cca_pre.transform(act_avg_A, act_avg_B)
  821. return proj_A_train[:, :Dim], proj_B_train[:, :Dim], proj_A_train_avg[:, :Dim], proj_B_train_avg[:, :Dim]
  822. elif methodname.lower() == "pls":
  823. if self.fit_method.lower() == "act_avg":
  824. fit_A, fit_B = act_avg_A, act_avg_B
  825. elif self.fit_method.lower() == "act_stack":
  826. fit_A, fit_B = act_stack_A, act_stack_B
  827. else:
  828. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  829. n_comp = min(fit_A.shape[1], fit_B.shape[1])
  830. self.method_pls_pre = PLS_SVD(n_components=n_comp)
  831. if self.fit_method.lower() == "act_avg":
  832. proj_A_train_avg, proj_B_train_avg = self.method_pls_pre.fit_transform(act_avg_A, act_avg_B)
  833. proj_A_train, proj_B_train = self.method_pls_pre.transform(act_stack_A, act_stack_B)
  834. else:
  835. proj_A_train, proj_B_train = self.method_pls_pre.fit_transform(act_stack_A, act_stack_B)
  836. proj_A_train_avg, proj_B_train_avg = self.method_pls_pre.transform(act_avg_A, act_avg_B)
  837. return proj_A_train[:, :Dim], proj_B_train[:, :Dim], proj_A_train_avg[:, :Dim], proj_B_train_avg[:, :Dim]
  838. else:
  839. raise ValueError("Unknown methodname. Choose 'pca', 'cca', or 'pls'.")
  840. def transform_stack_pre(self, act_stack_A, act_stack_B):
  841. if act_stack_A.ndim == 2:
  842. act_stack_A = act_stack_A[..., np.newaxis]
  843. if act_stack_B.ndim == 2:
  844. act_stack_B = act_stack_B[..., np.newaxis]
  845. T, _, n_trials = act_stack_A.shape
  846. proj_A = np.zeros((T, self.Dim_pre, n_trials))
  847. proj_B = np.zeros((T, self.Dim_pre, n_trials))
  848. method = self.method_pre.lower()
  849. for i in range(n_trials):
  850. X_A = act_stack_A[:, :, i]
  851. X_B = act_stack_B[:, :, i]
  852. if method == "pca":
  853. A_t = self.method_A_pre.transform(X_A)[:, :self.Dim_pre]
  854. B_t = self.method_B_pre.transform(X_B)[:, :self.Dim_pre]
  855. elif method == "cca" or method == "cca_ortho":
  856. A_t, B_t = self.method_cca_pre.transform(X_A, X_B)
  857. A_t = A_t[:, :self.Dim_pre]
  858. B_t = B_t[:, :self.Dim_pre]
  859. elif method == "pls":
  860. A_t, B_t = self.method_pls_pre.transform(X_A, X_B)
  861. A_t = A_t[:, :self.Dim_pre]
  862. B_t = B_t[:, :self.Dim_pre]
  863. else:
  864. raise ValueError("Unknown dim_method. Choose 'pca', 'cca', or 'pls'.")
  865. proj_A[:, :, i] = A_t
  866. proj_B[:, :, i] = B_t
  867. return proj_A, proj_B
  868. def reduce_dimension(self, act_avg_A, act_avg_B, act_stack_A, act_stack_B):
  869. if self.dim_method.lower() == "pca":
  870. act_avg_C = np.concatenate((act_avg_A, act_avg_B), axis=1)
  871. act_stack_C = np.concatenate((act_stack_A, act_stack_B), axis=1)
  872. self.method_A = PCA()
  873. self.method_B = PCA()
  874. self.method_C = PCA()
  875. if self.fit_method.lower() == "act_avg":
  876. self.method_A.fit(act_avg_A)
  877. self.method_B.fit(act_avg_B)
  878. self.method_C.fit(act_avg_C)
  879. elif self.fit_method.lower() == "act_stack":
  880. self.method_A.fit(act_stack_A)
  881. self.method_B.fit(act_stack_B)
  882. self.method_C.fit(act_stack_C)
  883. else:
  884. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  885. proj_A_train = self.method_A.transform(act_stack_A)
  886. proj_B_train = self.method_B.transform(act_stack_B)
  887. proj_C_train = self.method_C.transform(act_stack_C)
  888. return proj_A_train, proj_B_train, proj_C_train
  889. elif self.dim_method.lower() == "cca":
  890. if self.fit_method.lower() == "act_avg":
  891. fit_A, fit_B = act_avg_A, act_avg_B
  892. elif self.fit_method.lower() == "act_stack":
  893. fit_A, fit_B = act_stack_A, act_stack_B
  894. else:
  895. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  896. n_comp = min(fit_A.shape[1], fit_B.shape[1])
  897. self.method_cca = CCA_SVD(n_components=n_comp)
  898. self.method_cca.fit(fit_A, fit_B)
  899. proj_A_train, proj_B_train = self.method_cca.transform(act_stack_A, act_stack_B)
  900. return proj_A_train, proj_B_train
  901. elif self.dim_method.lower() == "cca_ortho":
  902. if self.fit_method.lower() == "act_avg":
  903. fit_A, fit_B = act_avg_A, act_avg_B
  904. elif self.fit_method.lower() == "act_stack":
  905. fit_A, fit_B = act_stack_A, act_stack_B
  906. else:
  907. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  908. n_comp = min(fit_A.shape[1], fit_B.shape[1])
  909. self.method_cca = CCA_ortho(n_components=n_comp)
  910. self.method_cca.fit(fit_A, fit_B)
  911. proj_A_train, proj_B_train = self.method_cca.transform(act_stack_A, act_stack_B)
  912. return proj_A_train, proj_B_train
  913. elif self.dim_method.lower() == "pls":
  914. if self.fit_method.lower() == "act_avg":
  915. fit_A, fit_B = act_avg_A, act_avg_B
  916. elif self.fit_method.lower() == "act_stack":
  917. fit_A, fit_B = act_stack_A, act_stack_B
  918. else:
  919. raise ValueError("Unknown fit_method. Choose 'act_avg' or 'act_stack'.")
  920. n_comp = min(fit_A.shape[1], fit_B.shape[1]) # Use fit_A and fit_B here
  921. self.method_pls = PLS_SVD(n_components=n_comp)
  922. self.method_pls.fit(fit_A, fit_B)
  923. proj_A_train, proj_B_train = self.method_pls.transform(act_stack_A, act_stack_B)
  924. return proj_A_train, proj_B_train
  925. else:
  926. raise ValueError("Unknown dim_method. Choose 'pca', 'cca', or 'pls'.")
  927. def reduce_dim_transform(self,act_A, act_B):
  928. if self.dim_method.lower() == "pca":
  929. act_A_trans=self.method_A.transform(act_A)
  930. act_B_trans=self.method_B.transform(act_B)
  931. elif self.dim_method.lower() == "cca" or self.dim_method.lower() == "cca_ortho":
  932. act_A_trans, act_B_trans=self.method_cca.transform(act_A, act_B)
  933. elif self.dim_method.lower() == "pls":
  934. act_A_trans, act_B_trans=self.method_pls.transform(act_A, act_B)
  935. return act_A_trans, act_B_trans
  936. def get_transfomation_matrix(self, rescale=False):
  937. # Determine pre-transformation matrices and means for both groups
  938. if self.method_pre.lower() == "pca":
  939. pre_trans_A = self.method_A_pre.components_.T
  940. pre_trans_B = self.method_B_pre.components_.T
  941. pre_mean_A = self.method_A_pre.mean_
  942. pre_mean_B = self.method_B_pre.mean_
  943. elif self.method_pre.lower() == "cca":
  944. pre_trans_A = self.method_cca_pre.x_weights_
  945. pre_trans_B = self.method_cca_pre.y_weights_
  946. pre_mean_A = self.method_cca_pre.x_mean_
  947. pre_mean_B = self.method_cca_pre.y_mean_
  948. elif self.method_pre.lower() == "cca_ortho":
  949. pre_mean_A = self.method_cca_pre.mean_a
  950. pre_mean_B = self.method_cca_pre.mean_b
  951. if rescale is True:
  952. # the projected data has std=1
  953. pre_trans_A = self.method_cca_pre.W_a
  954. pre_trans_B = self.method_cca_pre.W_b
  955. else:
  956. # coefficient norm is 1 (default)
  957. pre_trans_A = self.method_cca_pre.P_a
  958. pre_trans_B = self.method_cca_pre.P_b
  959. elif self.method_pre.lower() == "pls":
  960. pre_trans_A = self.method_pls_pre.weights_x
  961. pre_trans_B = self.method_pls_pre.weights_y
  962. pre_mean_A = self.x_mean
  963. pre_mean_B = self.y_mean
  964. else:
  965. raise ValueError("Unknown method_pre. Choose 'pca', 'cca', or 'pls'.")
  966. # Determine post-transformation matrices and means for both groups
  967. if self.dim_method.lower() == "pca":
  968. post_trans_A = self.method_A.components_.T
  969. post_trans_B = self.method_B.components_.T
  970. # For PCA we assume no extra mean subtraction
  971. post_mean_A = self.method_A.mean_
  972. post_mean_B = self.method_B.mean_
  973. elif self.dim_method.lower() == "cca":
  974. post_trans_A = self.method_cca.x_weights_
  975. post_trans_B = self.method_cca.y_weights_
  976. post_mean_A = self.method_cca.x_mean_
  977. post_mean_B = self.method_cca.y_mean_
  978. elif self.dim_method.lower() == "cca_ortho":
  979. post_mean_A = self.method_cca.mean_a
  980. post_mean_B = self.method_cca.mean_b
  981. if rescale is True:
  982. # the projected data has std=1
  983. post_trans_A = self.method_cca.W_a
  984. post_trans_B = self.method_cca.W_b
  985. else:
  986. # coefficient norm is 1 (default)
  987. post_trans_A = self.method_cca.P_a
  988. post_trans_B = self.method_cca.P_b
  989. elif self.dim_method.lower() == "pls":
  990. post_trans_A = self.method_pls.weights_x
  991. post_trans_B = self.method_pls.weights_y
  992. post_mean_A = self.method_pls.x_mean
  993. post_mean_B = self.method_pls.y_mean
  994. else:
  995. raise ValueError("Unknown dim_method. Choose 'pca', 'cca', 'cca_ortho', or 'pls'.")
  996. # Compose the overall transformation for group A and B
  997. linear_A = np.matmul(pre_trans_A[:,:np.shape(post_trans_A)[0]], post_trans_A)
  998. linear_B = np.matmul(pre_trans_B[:,:np.shape(post_trans_B)[0]], post_trans_B)
  999. bias_A = - (np.matmul(pre_mean_A, linear_A) + np.matmul(post_mean_A, post_trans_A))
  1000. bias_B = - (np.matmul(pre_mean_B, linear_B) + np.matmul(post_mean_B, post_trans_B))
  1001. # Return the overall transformation as lists for each group: [linear, bias]
  1002. return [linear_A, bias_A.reshape(1, -1)], [linear_B, bias_B.reshape(1, -1)]
  1003. def make_entire_trans_mat(self,nUnit, trans_A_sub, trans_B_sub, norm=False):
  1004. trans_A=np.zeros((nUnit,np.shape(trans_A_sub[0])[1]))
  1005. trans_B=np.zeros((nUnit,np.shape(trans_B_sub[0])[1]))
  1006. trans_A[self.act_log_A,:]=trans_A_sub[0] # (nUnit, Dim)
  1007. trans_B[self.act_log_B,:]=trans_B_sub[0]
  1008. bias_A=trans_A_sub[1] # (1, Dim)
  1009. bias_B=trans_B_sub[1]
  1010. norms_A=np.linalg.norm(trans_A, axis=0, keepdims=True)
  1011. norms_B=np.linalg.norm(trans_B, axis=0, keepdims=True)
  1012. # normalize transformation matrix
  1013. if norm is True:
  1014. trans_A=np.divide(trans_A, norms_A, where=norms_A!=0)
  1015. trans_B=np.divide(trans_B, norms_B, where=norms_B!=0)
  1016. bias_A=np.divide(bias_A, norms_A, where=norms_A!=0)
  1017. bias_B=np.divide(bias_B, norms_B, where=norms_B!=0)
  1018. return [trans_A,bias_A], [trans_B,bias_B]
  1019. def get_ortho_vec(self,mat,ind):
  1020. """
  1021. Returns the projection matrix onto the subspace orthogonal to all columns of `mat`
  1022. except for the column at index `ind`.
  1023. Parameters:
  1024. mat (np.ndarray): The input matrix of shape (n, m).
  1025. ind (int): The index of the column to exclude from the orthogonal projection.
  1026. Returns:
  1027. np.ndarray: The projection matrix onto the orthogonal complement of the subspace
  1028. spanned by all columns except the one at index `ind`.
  1029. """
  1030. cols = mat.shape[1]
  1031. size0=mat.shape[0]
  1032. new_order = [ind] + [j for j in range(cols) if j != ind]
  1033. mat_reordered = mat[:, new_order]
  1034. Q,R=np.linalg.qr(mat_reordered)
  1035. Q_sub=Q[:,1:]
  1036. ortho_mat=np.eye(size0)-Q_sub @ Q_sub.T
  1037. return ortho_mat
  1038. def get_ortho_subspace(self,mat,ind):
  1039. """
  1040. Returns the vector that is the projection of the column at index `ind` onto
  1041. the subspace orthogonal to all the other columns of `mat`.
  1042. Parameters:
  1043. mat (np.ndarray): The input matrix of shape (n, m) with n > m.
  1044. ind (int): The index of the column to exclude from the orthogonal projection.
  1045. Returns:
  1046. np.ndarray: A vector of shape (n,) that is the projection of mat[:, ind]
  1047. onto the null space of the other columns.
  1048. """
  1049. null=null_space(np.delete(mat.T,ind, axis=0))
  1050. vec_ortho=null@(null.T@mat[:,ind])
  1051. vec_ortho/=np.linalg.norm(vec_ortho,axis=0)
  1052. return vec_ortho
  1053. def get_ortho_mat(self,mat):
  1054. """
  1055. Returns a matrix whose i-th column is the projection of mat[:, i]
  1056. onto the subspace orthogonal to the other columns of mat.
  1057. Parameters:
  1058. mat (np.ndarray): The input matrix of shape (n, m) with n > m.
  1059. Returns:
  1060. np.ndarray: A matrix of shape (n, m), where the i-th column is the
  1061. orthogonal projection of mat[:, i] onto the null space
  1062. of the other columns.
  1063. """
  1064. mat_ortho=[]
  1065. for i in range(np.shape(mat)[1]):
  1066. vec_ortho=self.get_ortho_subspace(mat,i)
  1067. mat_ortho.append(vec_ortho)
  1068. return np.array(mat_ortho).T
  1069. def remove_component(self, proj_train, remove_ind):
  1070. if remove_ind==None:
  1071. return proj_train
  1072. else:
  1073. return np.delete(proj_train, remove_ind, axis=1)
  1074. def remove_component_stack(self, proj_list, remove_ind):
  1075. if remove_ind==None:
  1076. return proj_list
  1077. else:
  1078. return [np.delete(proj, remove_ind, axis=1) for proj in proj_list]
  1079. def make_rand_weight(self,x,n,ind, w):
  1080. """
  1081. for each timepoint in x, make vector of size n.
  1082. each vector is sampled randomly and first ind has weights w (0 to 1)
  1083. """
  1084. mat1=np.random.randn(x.shape[0], x.shape[1], ind)
  1085. mat2=np.random.randn(x.shape[0], x.shape[1], n-ind)
  1086. mat1/=np.linalg.norm(mat1,axis=2, keepdims=True)
  1087. mat2/=np.linalg.norm(mat2,axis=2, keepdims=True)
  1088. mat=np.concatenate((np.sqrt(w)*mat1, np.sqrt(1-w)*mat2), axis=2)
  1089. mat/=np.linalg.norm(mat, axis=2, keepdims=True)
  1090. return mat #(sample, time, n)
  1091. def create_train_classifier(self, proj_train, class_train, n_estimators=100, bootstrap=True, n_jobs=-1):
  1092. from sklearn.ensemble import RandomForestClassifier
  1093. proj_train_A = proj_train[0]
  1094. proj_train_B = proj_train[1]
  1095. if self.dim_method.lower() == "pca":
  1096. proj_train_C = proj_train[2]
  1097. clf_A = RandomForestClassifier(n_estimators=n_estimators, bootstrap=bootstrap, n_jobs=n_jobs)
  1098. clf_A.fit(proj_train_A[:, :self.Dim], class_train[0])
  1099. clf_B = RandomForestClassifier(n_estimators=n_estimators, bootstrap=bootstrap, n_jobs=n_jobs)
  1100. clf_B.fit(proj_train_B[:, :self.Dim], class_train[1])
  1101. self.clf_A = clf_A
  1102. self.clf_B = clf_B
  1103. if self.dim_method.lower() == "pca":
  1104. clf_C = RandomForestClassifier(n_estimators=n_estimators, bootstrap=bootstrap, n_jobs=n_jobs)
  1105. clf_C.fit(proj_train_C[:, :self.Dim], class_train[0])
  1106. self.clf_C = clf_C
  1107. return clf_A, clf_B, clf_C
  1108. else:
  1109. return clf_A, clf_B
  1110. def perturb_and_decode_noise_prob(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1111. sample_size, trial_num, inputdur, nInput,
  1112. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model):
  1113. x, y, In_ons = self.makeInOut_sameint(sample_size, trial_num, inputdur, nInput, order)
  1114. pert_ind = In_ons[:, [trial1]] + ind1
  1115. if stop:
  1116. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1117. model2 = self.build_model_perturb_noise_prob(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1118. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1119. pert_ind=pert_ind, pert_which=pert_which,
  1120. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau)
  1121. model2.set_weights(model.get_weights())
  1122. predictions = model2.predict(x)
  1123. outputs = [layer.output for layer in model2.layers[1:]] # Exclude input layer
  1124. activity_model2 = Model(inputs=model2.input, outputs=outputs)
  1125. output_and_activities2 = activity_model2.predict(x)
  1126. activities_A = output_and_activities2[0]
  1127. activities_B = output_and_activities2[1]
  1128. pert_ind_2 = np.zeros(pert_ind.shape)
  1129. In_ons_2 = np.zeros(In_ons[:, trial1:].shape)
  1130. trial2 = trial1
  1131. int_diff = int(np.round((In_ons[0, trial2] - In_ons[0, trial1]) / self.min_dur) * self.min_dur)
  1132. eightnum = int((trial_num - trial1) / 2)
  1133. actpart_A = np.zeros((activities_A.shape[0], eightnum * (self.min_dur + self.max_dur), activities_A.shape[2]))
  1134. actpart_B = np.zeros((activities_B.shape[0], eightnum * (self.min_dur + self.max_dur), activities_B.shape[2]))
  1135. predictions2 = np.zeros((predictions.shape[0], eightnum * (self.min_dur + self.max_dur), predictions.shape[2]))
  1136. for i in range(In_ons.shape[0]):
  1137. actpart_A[i, :, :] = activities_A[i, In_ons[i, trial2] : In_ons[i, trial2] + eightnum * (self.min_dur + self.max_dur), :]
  1138. actpart_B[i, :, :] = activities_B[i, In_ons[i, trial2] : In_ons[i, trial2] + eightnum * (self.min_dur + self.max_dur), :]
  1139. predictions2[i, :, :] = predictions[i, In_ons[i, trial2] : In_ons[i, trial2] + eightnum * (self.min_dur + self.max_dur), :]
  1140. pert_ind_2[i, :] = pert_ind[i, :] - (In_ons[i, trial2] - int_diff)
  1141. In_ons_2[i, :] = In_ons[i, trial1:] - (In_ons[i, trial2] - int_diff)
  1142. actpart_A = np.transpose(actpart_A, (1, 2, 0))
  1143. actpart_B = np.transpose(actpart_B, (1, 2, 0))
  1144. return actpart_A, actpart_B
  1145. def perturb_and_decode_noise_prob2(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1146. sample_size, trial_num, inputdur, nInput,
  1147. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model):
  1148. x, y, In_ons = self.makeInOut_sameint(sample_size, trial_num, inputdur, nInput, order)
  1149. pert_ind = In_ons[:, [trial1]] + ind1
  1150. if stop:
  1151. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1152. model2 = self.build_model_perturb_noise_prob(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1153. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1154. pert_ind=pert_ind, pert_which=pert_which,
  1155. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau)
  1156. model2.set_weights(model.get_weights())
  1157. predictions = model2.predict(x)
  1158. outputs = [layer.output for layer in model2.layers[1:]] # Exclude input layer
  1159. activity_model2 = Model(inputs=model2.input, outputs=outputs)
  1160. output_and_activities2 = activity_model2.predict(x)
  1161. activities_A = output_and_activities2[0]
  1162. activities_B = output_and_activities2[1]
  1163. # reshape the activities
  1164. actpart_A=self.concatAct_sliced(activities_A, In_ons)#batch, time, units
  1165. actpart_B=self.concatAct_sliced(activities_B, In_ons)
  1166. actpart_A = np.transpose(actpart_A, (1, 2, 0))# time, units, batch
  1167. actpart_B = np.transpose(actpart_B, (1, 2, 0))
  1168. return actpart_A, actpart_B
  1169. def perturb_and_decode_noise_prob2_brown(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1170. sample_size, trial_num, inputdur, nInput,
  1171. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1172. brown_scale, rank,exp=1,noise_weights=None):
  1173. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  1174. brown_scale=brown_scale, rank=rank, exp=exp)
  1175. self.x=x
  1176. self.In_ons=In_ons
  1177. pert_ind = In_ons[:, [trial1]] + ind1
  1178. self.pert_ind=pert_ind
  1179. if stop:
  1180. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1181. model2 = self.build_model_perturb_noise_prob_brown(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1182. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1183. pert_ind=pert_ind, pert_which=pert_which,
  1184. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau,
  1185. rank=rank,exp=exp,noise_weights=noise_weights)
  1186. model2.set_weights(model.get_weights())
  1187. predictions = model2.predict(x)
  1188. outputs = [layer.output for layer in model2.layers[1:]] # Exclude input layer
  1189. activity_model2 = Model(inputs=model2.input, outputs=outputs)
  1190. output_and_activities2 = activity_model2.predict(x)
  1191. activities_A = output_and_activities2[1]
  1192. activities_B = output_and_activities2[2]
  1193. self.activities_A=activities_A
  1194. self.activities_B=activities_B
  1195. # reshape the activities
  1196. actpart_A=self.concatAct_sliced(activities_A, In_ons)#batch, time, units
  1197. actpart_B=self.concatAct_sliced(activities_B, In_ons)
  1198. self.perturbed_output=output_and_activities2[5]
  1199. self.perturbed_output_y=y
  1200. x_part=self.concatAct_sliced(x, In_ons)#batch, time, units
  1201. self.x=x
  1202. self.x_part=x_part
  1203. #self.perturbed_output_all=output_and_activities2
  1204. actpart_A = np.transpose(actpart_A, (1, 2, 0))# time, units, batch
  1205. actpart_B = np.transpose(actpart_B, (1, 2, 0))
  1206. return actpart_A, actpart_B
  1207. def perturb_and_decode_noise_prob2_brown_dir(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1208. sample_size, trial_num, inputdur, nInput,
  1209. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1210. brown_scale, rank,exp=1,noise_weights=None, noise_vec=None,sync_noise=True):
  1211. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  1212. brown_scale=brown_scale, rank=rank, exp=exp)
  1213. pert_ind = In_ons[:, [trial1]] + ind1
  1214. if stop:
  1215. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1216. model2 = self.build_model_perturb_noise_prob_brown_dir(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1217. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1218. pert_ind=pert_ind, pert_which=pert_which,
  1219. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau,
  1220. rank=rank,exp=exp,noise_weights=noise_weights,
  1221. noise_vec=noise_vec,
  1222. sync_noise=sync_noise)
  1223. model2.set_weights(model.get_weights())
  1224. #predictions = model2.predict(x)
  1225. outputs = [layer.output for layer in model2.layers[1:]] # Exclude input layer
  1226. activity_model2 = Model(inputs=model2.input, outputs=outputs)
  1227. output_and_activities2 = activity_model2.predict(x)
  1228. activities_A = output_and_activities2[1]
  1229. activities_B = output_and_activities2[2]
  1230. # reshape the activities
  1231. actpart_A=self.concatAct_sliced(activities_A, In_ons)#batch, time, units
  1232. actpart_B=self.concatAct_sliced(activities_B, In_ons)
  1233. self.perturbed_output=output_and_activities2[5]
  1234. self.perturbed_output_y=y
  1235. #self.perturbed_output_all=output_and_activities2
  1236. actpart_A = np.transpose(actpart_A, (1, 2, 0))# time, units, batch
  1237. actpart_B = np.transpose(actpart_B, (1, 2, 0))
  1238. return actpart_A, actpart_B
  1239. def perturb_and_decode_noise_prob2_brown_dir_ratio(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1240. sample_size, trial_num, inputdur, nInput,
  1241. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1242. brown_scale, rank,exp=1,
  1243. noise_weights=None, pert_noise_mat=None,sync_noise=True,pert_w=0,bias_ind=2):
  1244. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  1245. brown_scale=brown_scale, rank=rank, exp=exp)
  1246. pert_ind = In_ons[:, [trial1]] + ind1
  1247. if stop:
  1248. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1249. # pert_noise_mat is a list of length 2 and each has shape (unit, 101)
  1250. # create noise weights for weighting the noise component
  1251. noise_w=self.make_rand_weight(x, pert_noise_mat[0].shape[1], bias_ind, pert_w)
  1252. x=np.concatenate((x,noise_w),axis=2) # sample, time, 3+100
  1253. model2 = self.build_model_perturb_noise_prob_brown_dir_ratio(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1254. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1255. pert_ind=pert_ind, pert_which=pert_which,
  1256. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau,
  1257. rank=rank,exp=exp,noise_weights=noise_weights,
  1258. pert_noise_mat=pert_noise_mat,
  1259. sync_noise=sync_noise)
  1260. model2.set_weights(model.get_weights())
  1261. #predictions = model2.predict(x)
  1262. outputs = [layer.output for layer in model2.layers[1:]] # Exclude input layer
  1263. activity_model2 = Model(inputs=model2.input, outputs=outputs)
  1264. output_and_activities2 = activity_model2.predict(x)
  1265. activities_A = output_and_activities2[1]
  1266. activities_B = output_and_activities2[2]
  1267. # reshape the activities
  1268. actpart_A=self.concatAct_sliced(activities_A, In_ons)#batch, time, units
  1269. actpart_B=self.concatAct_sliced(activities_B, In_ons)
  1270. self.perturbed_output=output_and_activities2[5]
  1271. self.perturbed_output_y=y
  1272. #self.perturbed_output_all=output_and_activities2
  1273. actpart_A = np.transpose(actpart_A, (1, 2, 0))# time, units, batch
  1274. actpart_B = np.transpose(actpart_B, (1, 2, 0))
  1275. return actpart_A, actpart_B
  1276. def activities_to_actpart(self,activities_A,In_ons):
  1277. actpart_A=self.concatAct_qq(activities_A, In_ons)#batch, time, units
  1278. actpart_A = np.transpose(actpart_A, (1, 2, 0))# time, units, batch
  1279. return actpart_A
  1280. def decode_time(self, actpart, clf_A=None, clf_B=None, clf_C=None, remove_ind=None):
  1281. actpart_A, actpart_B = actpart
  1282. # If actpart_A or actpart_B is 2D, expand dims to simulate shape[..., 1]
  1283. if actpart_A.ndim == 2:
  1284. actpart_A = np.expand_dims(actpart_A, axis=2)
  1285. if actpart_B.ndim == 2:
  1286. actpart_B = np.expand_dims(actpart_B, axis=2)
  1287. actpart_C = np.concatenate((actpart_A, actpart_B), axis=1)
  1288. pred_A = np.zeros((actpart_A.shape[0], actpart_A.shape[2]))
  1289. pred_B = np.zeros((actpart_B.shape[0], actpart_B.shape[2]))
  1290. if self.dim_method.lower() == "pca":
  1291. pred_C = np.zeros((actpart_B.shape[0], actpart_B.shape[2]))
  1292. for i in range(actpart_A.shape[2]):
  1293. proj_A = self.method_A.transform(actpart_A[:, :, i])
  1294. proj_B = self.method_B.transform(actpart_B[:, :, i])
  1295. proj_C = self.method_C.transform(actpart_C[:, :, i])
  1296. proj_A, proj_B, proj_C = self.remove_component_stack([proj_A, proj_B, proj_C],remove_ind)
  1297. if clf_A==None:
  1298. pred_A[:, i] = self.clf_A.predict(proj_A[:, :self.Dim])
  1299. else:
  1300. pred_A[:, i] = clf_A.predict(proj_A[:, :self.Dim])
  1301. if clf_B==None:
  1302. pred_B[:, i] = self.clf_B.predict(proj_B[:, :self.Dim])
  1303. else:
  1304. pred_B[:, i] = clf_B.predict(proj_B[:, :self.Dim])
  1305. if clf_C==None:
  1306. pred_C[:, i] = self.clf_C.predict(proj_C[:, :self.Dim])
  1307. else:
  1308. pred_C[:, i] =clf_C.predict(proj_C[:, :self.Dim])
  1309. return pred_A, pred_B, pred_C
  1310. elif self.dim_method.lower() == "cca":
  1311. for i in range(actpart_A.shape[2]):
  1312. proj_A, proj_B = self.method_cca.transform(actpart_A[:, :, i], actpart_B[:, :, i])
  1313. proj_A, proj_B = self.remove_component_stack([proj_A, proj_B],remove_ind)
  1314. if clf_A==None:
  1315. pred_A[:, i] = self.clf_A.predict(proj_A[:, :self.Dim])
  1316. else:
  1317. pred_A[:, i] = clf_A.predict(proj_A[:, :self.Dim])
  1318. if clf_B==None:
  1319. pred_B[:, i] = self.clf_B.predict(proj_B[:, :self.Dim])
  1320. else:
  1321. pred_B[:, i] = clf_B.predict(proj_B[:, :self.Dim])
  1322. return pred_A, pred_B
  1323. elif self.dim_method.lower() == "pls":
  1324. for i in range(actpart_A.shape[2]):
  1325. proj_A, proj_B = self.method_pls.transform(actpart_A[:, :, i], actpart_B[:, :, i])
  1326. proj_A, proj_B = self.remove_component_stack([proj_A, proj_B],remove_ind)
  1327. if clf_A==None:
  1328. pred_A[:, i] = self.clf_A.predict(proj_A[:, :self.Dim])
  1329. else:
  1330. pred_A[:, i] = clf_A.predict(proj_A[:, :self.Dim])
  1331. if clf_B==None:
  1332. pred_B[:, i] = self.clf_B.predict(proj_B[:, :self.Dim])
  1333. else:
  1334. pred_B[:, i] = clf_B.predict(proj_B[:, :self.Dim])
  1335. return pred_A, pred_B
  1336. def xlogx(self, p):
  1337. """ Compute p*log(p) while handling p=0 cases properly. """
  1338. a = np.zeros_like(p)
  1339. valid = p > 0 # Only apply log where p > 0
  1340. a[valid] = p[valid] * np.log(p[valid])
  1341. return a
  1342. def Seq_ind(self,act_avg_A,mean_range):
  1343. """
  1344. act_avg_A: (time, ncells)
  1345. """
  1346. data6 = act_avg_A[:self.min_dur, :]
  1347. data12 =act_avg_A[self.min_dur:, :]
  1348. ncell = data6.shape[1]
  1349. # Reduce bins
  1350. #mean_range = 50
  1351. data6_2 = np.zeros((data6.shape[0] // mean_range, data6.shape[1]))
  1352. data12_2 = np.zeros((data12.shape[0] // mean_range, data12.shape[1]))
  1353. for k in range(data6_2.shape[0]):
  1354. data6_2[k, :] = np.mean(data6[mean_range * k:mean_range * (k + 1), :], axis=0)
  1355. for k in range(data12_2.shape[0]):
  1356. data12_2[k, :] = np.mean(data12[mean_range * k:mean_range * (k + 1), :], axis=0)
  1357. data6 = data6_2
  1358. data12 = data12_2
  1359. maxind6 = np.argmax(data6, axis=0)
  1360. maxind12 = np.argmax(data12, axis=0)
  1361. onehot6 = np.zeros_like(data6)
  1362. onehot12 = np.zeros_like(data12)
  1363. for k in range(data6.shape[1]):
  1364. onehot6[maxind6[k], k] = 1
  1365. for k in range(data12.shape[1]):
  1366. onehot12[maxind12[k], k] = 1
  1367. p6 = np.sum(onehot6, axis=1) / ncell
  1368. p12 = np.sum(onehot12, axis=1) / ncell
  1369. peak_ent=np.zeros(2)
  1370. temp_spar=np.zeros(2)
  1371. Sql=np.zeros(2)
  1372. peak_ent[0] = np.sum(-self.xlogx(p6)) / np.log(data6.shape[0])
  1373. peak_ent[1]= np.sum(-self.xlogx(p12)) / np.log(data12.shape[0])
  1374. data6_norm = data6 / np.sum(data6, axis=1, keepdims=True)
  1375. data12_norm = data12 / np.sum(data12, axis=1, keepdims=True)
  1376. temp_spar[0] = 1 - np.mean(np.sum(-self.xlogx(data6_norm), axis=1)) / np.log(ncell)
  1377. temp_spar[1] = 1 - np.mean(np.sum(-self.xlogx(data12_norm), axis=1)) / np.log(ncell)
  1378. Sql[0] = np.sqrt(peak_ent[0] * temp_spar[0])
  1379. Sql[1] = np.sqrt(peak_ent[1] * temp_spar[1])
  1380. return peak_ent, temp_spar, Sql
  1381. # create models for calculating lyapunov exponents
  1382. def build_model_perturb_noise_prob_brown_lyapunov(self,nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1383. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=1,noise_weights=None,
  1384. stateful=False,start=0, option="lyapunov",test_mat=None, update_state=True):
  1385. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1386. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1387. if stateful is False:
  1388. visible = Input(shape=(None,nInput+rank))
  1389. else:
  1390. visible = Input(batch_shape=(sample_size, None, nInput+rank))
  1391. if noise_weights is None:
  1392. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  1393. print('Random noise weight created')
  1394. else:
  1395. self.noise_weights=noise_weights.copy()
  1396. rnn_cell = RNNCustom2FixPerturb_noise_prob_brown_lyapunov(
  1397. nUnit,
  1398. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1399. input_activation=lambda x: tf.where(x > 0, x, ReLUalpha * x),
  1400. use_bias=False,
  1401. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1402. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1403. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1404. kernel_trainable=True,
  1405. seed=seed1,
  1406. tau=tau,
  1407. noisesd=0.08,
  1408. perturb_ind=pert_ind,
  1409. pert_which=pert_which,
  1410. pert_noisesd=pert_noisesd,
  1411. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1412. )
  1413. # Create the RNN layer with your custom cell and set it to stateless
  1414. rnn_layer = RNN(
  1415. rnn_cell,
  1416. stateful=stateful, # now stateless
  1417. return_sequences=True,
  1418. activity_regularizer=l2(0.1)
  1419. )
  1420. # Call the RNN layer; initial states will be automatically set to zeros
  1421. outputs = rnn_layer(visible)
  1422. # Here, outputs is a list of two tensors.
  1423. hidden_outputs = outputs[0] # shape: (batch, timesteps, 2*nUnit)
  1424. Jacobian = outputs[1] # shape: (batch, timesteps, 4*nUnit^2)
  1425. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  1426. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  1427. # Define the output layers
  1428. output_A = Dense(2, activation='tanh',
  1429. kernel_initializer=GlorotUniform(seed=seed1),
  1430. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  1431. output_B = Dense(2, activation='tanh',
  1432. kernel_initializer=GlorotUniform(seed=seed1),
  1433. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  1434. # Concatenate the outputs
  1435. output = Concatenate(axis=-1)([output_A, output_B])
  1436. if option.lower()=="lyapunov":
  1437. QRDCell=QRDcell2_flat_2(nUnit=nUnit,start=start)
  1438. #hidden1[2] = tf.ensure_shape(hidden1[2], [batch_size, None, 4 * nUnit**2])
  1439. Rlayer=RNN(QRDCell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1440. Rmat=Rlayer(Jacobian) #size(batch, timesteps, 2*nUnit)
  1441. model = Model(inputs=visible, outputs=[output,Rmat])
  1442. elif option.lower()=="norm_scale":
  1443. norm_cell=Get_norm_ratio(nUnit=nUnit, start=start, test_mat=test_mat, update_state=update_state)
  1444. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1445. # Make sure to use the same dtype you expect (typically tf.float32)
  1446. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1447. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1448. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, timesteps, ncol)
  1449. model = Model(inputs=visible, outputs=[output,Rmat])
  1450. elif option.lower()=="lyap_dir" or option.lower()=="cca_diff_lyap":
  1451. norm_cell=Get_lyap_dir(nUnit=nUnit, start=start, test_mat=test_mat, update_state=update_state)
  1452. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1453. # Make sure to use the same dtype you expect (typically tf.float32)
  1454. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1455. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1456. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, timesteps, ncol)
  1457. model = Model(inputs=visible, outputs=[output,Rmat])
  1458. elif option.lower()=="cca_ax_diff":
  1459. u,v=tf.split(test_mat, num_or_size_splits=2, axis=0)
  1460. LE_out=LE_axis_diff(nUnit*2, u, v)(Jacobian)
  1461. model = Model(inputs=visible, outputs=[output,LE_out])
  1462. elif option.lower()=="jxy":
  1463. u,v=tf.split(test_mat, num_or_size_splits=2, axis=0)
  1464. LE_out=Jxy_val(nUnit*2, u, v)(Jacobian)
  1465. model = Model(inputs=visible, outputs=[output,LE_out])
  1466. elif option.lower()=="none":
  1467. Rmat=None
  1468. model = Model(inputs=visible, outputs=[output])
  1469. elif option.lower()=="jacobian":
  1470. model = Model(inputs=visible, outputs=[output, Jacobian])
  1471. elif option.lower()=="jacobian_avg":
  1472. norm_cell=CumulativeJacobian(nUnit=nUnit, start=start)
  1473. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1474. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1475. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=False)# (batch_num,timestep,2*nUnits)
  1476. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, 1, (2*nUnit)^2)
  1477. model = Model(inputs=visible, outputs=[output, Rmat])
  1478. elif option.lower()=="eigen":
  1479. norm_cell=EigOrSingJacobian(nUnit=nUnit, start=start, calc_eig=True)
  1480. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1481. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1482. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1483. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, 1, (2*nUnit)^2)
  1484. model = Model(inputs=visible, outputs=[output, Rmat])
  1485. elif option.lower()=="singular":
  1486. norm_cell=EigOrSingJacobian(nUnit=nUnit, start=start, calc_eig=False)
  1487. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1488. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1489. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1490. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, 1, (2*nUnit)^2)
  1491. model = Model(inputs=visible, outputs=[output, Rmat])
  1492. else:
  1493. warnings.warn(f"Unknown option: {option}")
  1494. return model
  1495. def run_model_with_jacobian(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1496. sample_size, trial_num, inputdur, nInput,
  1497. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1498. brown_scale, rank, exp=1, noise_weights=None,stateful=False, start=None,
  1499. option="lyapunov",test_mat=None, batch_size=10, update_state=True,
  1500. lr_AB=[None, None], lr_BA=[None,None],jacob_at_zero=False, project_mat=None):
  1501. # Create input and output arrays using the helper function.
  1502. if stateful is False:
  1503. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  1504. brown_scale=brown_scale, rank=rank, exp=exp)
  1505. else:
  1506. x, y, In_ons, Wbin= self.makeInOut_sameint_brown_stateful(sample_size, trial_num,
  1507. inputdur, nInput,
  1508. order,brown_scale=brown_scale,
  1509. rank=rank, exp=exp,
  1510. batch_num=batch_size)
  1511. if start is None:
  1512. start=In_ons[0,2]
  1513. self.start=start
  1514. # Compute the perturbation indices.
  1515. pert_ind = In_ons[:, [trial1]] + ind1
  1516. # Optionally modify the input if stop is True.
  1517. if stop:
  1518. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1519. # Build a new model that computes the lyapunov exponents with the provided parameters.
  1520. if option.lower()=="noise_sing" or option.lower()=="noise_lyap_dir":
  1521. model2 = self.build_model_perturb_noise_prob_brown_lyapunov_noise(
  1522. nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1523. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=exp,noise_weights=noise_weights,
  1524. stateful=stateful,start=start,option=option,
  1525. test_mat=test_mat,update_state=update_state, project_mat=project_mat
  1526. )
  1527. elif option.lower()=="synch_lyapunov" or option.lower()=="synch_norm_scale" or option.lower()=="synch_lyap_dir":
  1528. model2 = self.build_model_synchronicity_jacobian(
  1529. nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1530. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=exp,noise_weights=noise_weights,
  1531. stateful=stateful,start=start,option=option,
  1532. test_mat=test_mat,update_state=update_state,
  1533. lr_AB=lr_AB, lr_BA=lr_BA,jacob_at_zero=jacob_at_zero
  1534. )
  1535. else:
  1536. model2 = self.build_model_perturb_noise_prob_brown_lyapunov(
  1537. nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1538. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=exp,noise_weights=noise_weights,
  1539. stateful=stateful,start=start,option=option,
  1540. test_mat=test_mat,update_state=update_state
  1541. )
  1542. # Copy the weights from the provided model.
  1543. self.model2=model2
  1544. model2.set_weights(model.get_weights())
  1545. model2.reset_states()
  1546. # Run the prediction.
  1547. if stateful is False:
  1548. predictions = model2.predict(x) #(output, Rmat, A_activity, B_activity
  1549. else:
  1550. for i in range(len(x)):
  1551. print(f"prediction: {i} out of {len(x)}")
  1552. xin = tf.convert_to_tensor(x[i], dtype=tf.float32)
  1553. xin=tf.stop_gradient(xin)
  1554. pred = model2.predict_on_batch(xin) # pred = (output, Rmat, A_activity, B_activity)
  1555. if not isinstance(pred, (list, tuple)):
  1556. pred = [pred] #1, sample_size, time, output_dim
  1557. if i==0:
  1558. outputs_split = [[] for _ in range(len(pred))]
  1559. for j in range(len(pred)):
  1560. outputs_split[j].append(pred[j])
  1561. # Concatenate each type of output across time (axis=1)
  1562. predictions = [np.concatenate(output_list, axis=1) for output_list in outputs_split]
  1563. self.In_ons_temp=In_ons
  1564. return predictions #
  1565. def get_lyapunov(self, Rmat, start):
  1566. if start is None:
  1567. start=self.start
  1568. #Rmat: (batch, timesteps, 2*nUnit(or ncol))
  1569. Rmat=Rmat[:,start+1:,:]#(batch, time, output_dim)
  1570. Rmat=tf.math.abs(Rmat)
  1571. Rmat=tf.math.log(Rmat)
  1572. Rmat=tf.math.cumsum(Rmat,1)
  1573. Rmat=tf.math.reduce_mean(Rmat,0)#(time, output__dim)
  1574. timepoints=tf.shape(Rmat)[0]
  1575. v=tf.reshape(tf.cast(tf.range(timepoints),dtype=tf.float32), (-1, 1))#(time, 1)
  1576. v+=1
  1577. # take the mean
  1578. Rmat=Rmat/v
  1579. return Rmat # (time, output_dim=2*nUnit)
  1580. def get_lyapunov_along_axis(self,Rmat,start):
  1581. if start is None:
  1582. start=self.start
  1583. #Rmat: (batch, timesteps, 2*nUnit(or ncol))
  1584. Rmat=Rmat[:,start+1:,:]#(batch, time, ncol)
  1585. Rmat=tf.math.abs(Rmat)
  1586. Rmat=tf.math.log(Rmat)
  1587. Rmat=tf.math.reduce_mean(Rmat,0)#(time, output__dim)
  1588. return Rmat #(time, ncol) log ratio for each timestep
  1589. def build_model_synchronicity_jacobian(self,nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1590. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=1,noise_weights=None,
  1591. stateful=False,start=0, option="synch",test_mat=None, update_state=True,
  1592. lr_AB=[None, None], lr_BA=[None,None],jacob_at_zero=False ):
  1593. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1594. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1595. if stateful is False:
  1596. visible = Input(shape=(None,nInput+rank))
  1597. else:
  1598. visible = Input(batch_shape=(sample_size, None, nInput+rank))
  1599. if noise_weights is None:
  1600. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  1601. print('Random noise weight created')
  1602. else:
  1603. self.noise_weights=noise_weights.copy()
  1604. rnn_cell = RNNCustom2FixPerturb_noise_prob_synch_jacobian(
  1605. nUnit,
  1606. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1607. input_activation=lambda x: tf.where(x > 0, x, ReLUalpha * x),
  1608. use_bias=False,
  1609. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1610. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1611. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1612. kernel_trainable=True,
  1613. seed=seed1,
  1614. tau=tau,
  1615. noisesd=0.08,
  1616. perturb_ind=pert_ind,
  1617. pert_which=pert_which,
  1618. pert_noisesd=pert_noisesd,
  1619. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1620. lr_AB=lr_AB,
  1621. lr_BA=lr_BA,
  1622. jacob_at_zero=jacob_at_zero,
  1623. )
  1624. # Create the RNN layer with your custom cell and set it to stateless
  1625. rnn_layer = RNN(
  1626. rnn_cell,
  1627. stateful=stateful, # now stateless
  1628. return_sequences=True,
  1629. activity_regularizer=l2(0.1)
  1630. )
  1631. # Call the RNN layer; initial states will be automatically set to zeros
  1632. outputs = rnn_layer(visible)
  1633. # Here, outputs is a list of two tensors.
  1634. hidden_outputs = outputs[0] # shape: (batch, timesteps, 2*nUnit)
  1635. J_delta_A = outputs[1] # shape: (batch, timesteps, 4*nUnit^2)
  1636. J_delta_B = outputs[2] # shape: (batch, timesteps, 4*nUnit^2)
  1637. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  1638. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  1639. # Define the output layers
  1640. output_A = Dense(2, activation='tanh',
  1641. kernel_initializer=GlorotUniform(seed=seed1),
  1642. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  1643. output_B = Dense(2, activation='tanh',
  1644. kernel_initializer=GlorotUniform(seed=seed1),
  1645. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  1646. # Concatenate the outputs
  1647. output = Concatenate(axis=-1)([output_A, output_B])
  1648. nUnit_half=int(nUnit/2)
  1649. if option.lower()=="synch_lyapunov":
  1650. QRDCell_A=QRDcell2_flat_half(nUnit=nUnit,start=start)
  1651. #hidden1[2] = tf.ensure_shape(hidden1[2], [batch_size, None, 4 * nUnit**2])
  1652. Rlayer_A=RNN(QRDCell_A,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1653. Rmat_A=Rlayer_A(J_delta_A) #size(batch, timesteps, 2*nUnit)
  1654. QRDCell_B=QRDcell2_flat_half(nUnit=nUnit,start=start)
  1655. #hidden1[2] = tf.ensure_shape(hidden1[2], [batch_size, None, 4 * nUnit**2])
  1656. Rlayer_B=RNN(QRDCell_B,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1657. Rmat_B=Rlayer_B(J_delta_B) #size(batch, timesteps, 2*nUnit)
  1658. model = Model(inputs=visible, outputs=[output,Rmat_A, Rmat_B])
  1659. elif option.lower()=="synch_norm_scale":
  1660. test_mat_A, test_mat_B=np.split(test_mat,indices_or_sections=2, axis=0)
  1661. norm_cell=Get_norm_ratio(nUnit=nUnit_half, start=start, test_mat=test_mat_A, update_state=update_state)
  1662. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1663. # Make sure to use the same dtype you expect (typically tf.float32)
  1664. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1665. norm_cell_A=Get_norm_ratio(nUnit=nUnit_half, start=start, test_mat=test_mat_A, update_state=update_state)
  1666. Rlayer_A=RNN(norm_cell_A,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1667. Rmat_A = Rlayer_A(J_delta_A, initial_state=initial_state) #size(batch, timesteps, ncol)
  1668. norm_cell_B=Get_norm_ratio(nUnit=nUnit_half, start=start, test_mat=test_mat_B, update_state=update_state)
  1669. Rlayer_B=RNN(norm_cell_B,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1670. Rmat_B = Rlayer_B(J_delta_B, initial_state=initial_state) #size(batch, timesteps, ncol)
  1671. model = Model(inputs=visible, outputs=[output,Rmat_A, Rmat_B])
  1672. elif option.lower()=="synch_lyap_dir":
  1673. test_mat_A, test_mat_B=np.split(test_mat,indices_or_sections=2, axis=0)
  1674. norm_cell=Get_lyap_dir(nUnit=nUnit_half, start=start, test_mat=test_mat_A, update_state=update_state)
  1675. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1676. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1677. # Make sure to use the same dtype you expect (typically tf.float32)
  1678. norm_cell_A=Get_lyap_dir(nUnit=nUnit_half, start=start, test_mat=test_mat_A, update_state=update_state)
  1679. Rlayer_A=RNN(norm_cell_A,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1680. Rmat_A = Rlayer_A(J_delta_A, initial_state=initial_state) #size(batch, timesteps, ncol)
  1681. norm_cell_B=Get_lyap_dir(nUnit=nUnit_half, start=start, test_mat=test_mat_B, update_state=update_state)
  1682. Rlayer_B=RNN(norm_cell_B,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1683. Rmat_B = Rlayer_B(J_delta_B, initial_state=initial_state) #size(batch, timesteps, ncol)
  1684. model = Model(inputs=visible, outputs=[output,Rmat_A,Rmat_B])
  1685. return model
  1686. def build_model_perturb_noise_prob_brown_lyapunov_noise(self,nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1687. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=1,noise_weights=None,
  1688. stateful=False,start=0, option="lyapunov",test_mat=None, project_mat=None, update_state=True):
  1689. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1690. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1691. if stateful is False:
  1692. visible = Input(shape=(None,nInput+rank))
  1693. else:
  1694. visible = Input(batch_shape=(sample_size, None, nInput+rank))
  1695. if noise_weights is None:
  1696. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  1697. print('Random noise weight created')
  1698. else:
  1699. self.noise_weights=noise_weights.copy()
  1700. if option.lower()=="noise_sing":
  1701. rnn_cell = RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian(
  1702. nUnit,
  1703. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1704. input_activation=lambda x: tf.where(x > 0, x, ReLUalpha * x),
  1705. use_bias=False,
  1706. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1707. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1708. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1709. kernel_trainable=True,
  1710. seed=seed1,
  1711. tau=tau,
  1712. noisesd=0.08,
  1713. perturb_ind=pert_ind,
  1714. pert_which=pert_which,
  1715. pert_noisesd=pert_noisesd,
  1716. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1717. )
  1718. elif option.lower()=="noise_lyap_dir":
  1719. rnn_cell = RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_2(
  1720. nUnit,
  1721. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1722. input_activation=lambda x: tf.where(x > 0, x, ReLUalpha * x),
  1723. use_bias=False,
  1724. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1725. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1726. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1727. kernel_trainable=True,
  1728. seed=seed1,
  1729. tau=tau,
  1730. noisesd=0.08,
  1731. perturb_ind=pert_ind,
  1732. pert_which=pert_which,
  1733. pert_noisesd=pert_noisesd,
  1734. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1735. noise_mat=test_mat,#[( m,512), (m,512)]
  1736. project_mat=project_mat,
  1737. )
  1738. # Create the RNN layer with your custom cell and set it to stateless
  1739. rnn_layer = RNN(
  1740. rnn_cell,
  1741. stateful=stateful, # now stateless
  1742. return_sequences=True,
  1743. activity_regularizer=l2(0.1)
  1744. )
  1745. # Call the RNN layer; initial states will be automatically set to zeros
  1746. outputs = rnn_layer(visible)
  1747. # Here, outputs is a list of two tensors.
  1748. hidden_outputs = outputs[0] # shape: (batch, timesteps, 2*nUnit)
  1749. Jacobian = outputs[1] # shape: (batch, timesteps, 2*nUnit*2)
  1750. Jacobian_sub=outputs[2]
  1751. # Since the custom cell returns concatenated outputs, split them back into po_A and po_B sequences
  1752. po_A_sequence, po_B_sequence = tf.split(hidden_outputs, num_or_size_splits=2, axis=-1)
  1753. # Define the output layers
  1754. output_A = Dense(2, activation='tanh',
  1755. kernel_initializer=GlorotUniform(seed=seed1),
  1756. kernel_constraint=IEWeightOut(nInh=nInh))(po_A_sequence)
  1757. output_B = Dense(2, activation='tanh',
  1758. kernel_initializer=GlorotUniform(seed=seed1),
  1759. kernel_constraint=IEWeightOut(nInh=nInh))(po_B_sequence)
  1760. # Concatenate the outputs
  1761. output = Concatenate(axis=-1)([output_A, output_B])
  1762. if option.lower()=="noise_sing":
  1763. norm_cell=Noise_jacob(nUnit=nUnit, start=start, sing_or_eig=True)
  1764. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1765. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1766. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1767. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, 1, (2*nUnit)^2)
  1768. norm_cell_2=Noise_jacob(nUnit=nUnit, start=start, sing_or_eig=True)
  1769. dummy_input_2 = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1770. initial_state_2 = norm_cell_2.get_initial_state(inputs=dummy_input_2, batch_size=sample_size, dtype=tf.float32)
  1771. Rlayer_2=RNN(norm_cell_2,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1772. Rmat_2 = Rlayer_2(Jacobian_sub, initial_state=initial_state_2) #size(batch, 1, (2*nUnit)^2)
  1773. model = Model(inputs=visible, outputs=[output, Rmat, Rmat_2])
  1774. elif option.lower()=="noise_lyap_dir":
  1775. #outputs_state=outputs[0]
  1776. jacobian_A_flat=outputs[1] #(batch, m,m)
  1777. jacobian_B_flat=outputs[2] #(batch, m,m)
  1778. jacobian_A_flat_sub=outputs[3] #(batch, m,m)
  1779. jacobian_B_flat_sub=outputs[4] #(batch, m,m)
  1780. m=test_mat[0].shape[0]
  1781. diag_A=DiagonalLayer(m)(jacobian_A_flat) # shape = (batch_size, n)
  1782. diag_B=DiagonalLayer(m)(jacobian_B_flat) # shape = (batch_size, n)
  1783. diag_A_sub=DiagonalLayer(m)(jacobian_A_flat_sub) # shape = (batch_size, n)
  1784. diag_B_sub=DiagonalLayer(m)(jacobian_B_flat_sub) # shape = (batch_size, n)
  1785. model = Model(inputs=visible, outputs=[output, diag_A, diag_B, diag_A_sub, diag_B_sub])
  1786. return model
  1787. # perturb the model and record correlation of the activity with the perturbation noise
  1788. def build_model_perturb_noise_prob_brown_corr(self, nUnit, nInh, nInput, con_prob, maxval, ReLUalpha,
  1789. pert_ind, pert_which, seed1, pert_noisesd, tau, rank, exp, sample_size,
  1790. stateful=True, noise_weights=None, noise_vec=None, sync_noise=True,
  1791. option="running_corr",mat_AB=None):
  1792. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1793. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1794. if stateful is False:
  1795. visible = Input(shape=(None,nInput+rank))
  1796. else:
  1797. visible = Input(batch_shape=(sample_size, None, nInput+rank))
  1798. if noise_weights is None:
  1799. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  1800. print('Random noise weight created')
  1801. else:
  1802. self.noise_weights=noise_weights.copy()
  1803. rnn_cell = RNNCustom2FixPerturb_noise_dir_prob_brown_return_noise(
  1804. nUnit,
  1805. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1806. input_activation=tf.keras.layers.LeakyReLU(alpha=ReLUalpha),
  1807. use_bias=False,
  1808. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1809. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1810. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1811. kernel_trainable=True,
  1812. seed=seed1,
  1813. tau=tau,
  1814. noisesd=0.08,
  1815. perturb_ind=pert_ind,
  1816. pert_which=pert_which,
  1817. pert_noisesd=pert_noisesd,
  1818. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1819. noise_vec=noise_vec,
  1820. sync_noise=sync_noise,
  1821. )
  1822. # Create the RNN layer with your custom cell and set it to stateless
  1823. rnn_layer = RNN(
  1824. rnn_cell,
  1825. stateful=stateful, # now stateless
  1826. return_sequences=True,
  1827. activity_regularizer=l2(0.1)
  1828. )
  1829. # Call the RNN layer; initial states will be automatically set to zeros
  1830. rnn_out = rnn_layer(visible)
  1831. po_A, po_B, noise_add_A, noise_add_B, bin_A, bin_B=rnn_out
  1832. merged = Concatenate(axis=-1)(
  1833. [po_A, po_B, noise_add_A, noise_add_B, bin_A, bin_B]
  1834. ) # shape = (batch, time, 512+512+512+512+1+1)
  1835. if option.lower()=="running_corr":
  1836. corr_cell=Running_correlation(nUnit=nUnit)
  1837. # 2) Build your stateful RNN exactly once (no initial_state kwarg)
  1838. corr_rnn_layer = RNN(
  1839. corr_cell,
  1840. stateful=stateful, # True
  1841. return_sequences=True,
  1842. )
  1843. # 3) Call it on the merged tensor
  1844. corr_RNN = corr_rnn_layer(merged)
  1845. elif option.lower()=="cca_values":
  1846. output_dim=tf.shape(mat_AB[0][0])[1]
  1847. trans_A=mat_AB[0]
  1848. trans_B=mat_AB[1]
  1849. cca_A = Dense(
  1850. units=output_dim,
  1851. use_bias=True, # or True if you also want a fixed bias
  1852. kernel_initializer=initializers.Constant(trans_A[0]),
  1853. bias_initializer=initializers.Constant(trans_A[1]),
  1854. trainable=False
  1855. )(po_A)
  1856. cca_B = Dense(
  1857. units=output_dim,
  1858. use_bias=True, # or True if you also want a fixed bias
  1859. kernel_initializer=initializers.Constant(trans_B[0]),
  1860. bias_initializer=initializers.Constant(trans_B[1]),
  1861. trainable=False
  1862. )(po_B)
  1863. corr_RNN=Concatenate(axis=-1)([cca_A , cca_B])
  1864. # Define the output layers
  1865. output_A = Dense(2, activation='tanh',
  1866. kernel_initializer=GlorotUniform(seed=seed1),
  1867. kernel_constraint=IEWeightOut(nInh=nInh))(po_A)
  1868. output_B = Dense(2, activation='tanh',
  1869. kernel_initializer=GlorotUniform(seed=seed1),
  1870. kernel_constraint=IEWeightOut(nInh=nInh))(po_B)
  1871. # Concatenate the outputs
  1872. output = Concatenate(axis=-1)([output_A, output_B])
  1873. bin_AB = Concatenate(axis=-1)([bin_A, bin_B])
  1874. # out: (batch, time, -> )4, 2, 2
  1875. model = Model(inputs=visible, outputs=[output, corr_RNN, bin_AB])
  1876. return model
  1877. def perturb_noise_corr(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1878. sample_size, trial_num, inputdur, nInput,
  1879. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1880. brown_scale, rank, exp, batch_size, stateful=True,
  1881. noise_weights=None, noise_vec=None,sync_noise=True,
  1882. option="running_corr",mat_AB=None):
  1883. # perturb state along noise_vec
  1884. # if option is cca_values, then the state is projected to dimension specified by mat_AB and the difference is evaluated
  1885. # Create input and output arrays using the helper function.
  1886. if stateful is False:
  1887. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  1888. brown_scale=brown_scale, rank=rank, exp=exp)
  1889. else:
  1890. x, y, In_ons, Wbin= self.makeInOut_sameint_brown_stateful(sample_size, trial_num,
  1891. inputdur, nInput,
  1892. order,brown_scale=brown_scale,
  1893. rank=rank, exp=exp,
  1894. batch_num=batch_size)
  1895. start=In_ons[0,2]
  1896. self.start=start
  1897. # Compute the perturbation indices.
  1898. pert_ind = In_ons[:, [trial1]] + ind1
  1899. # Optionally modify the input if stop is True.
  1900. if stop:
  1901. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  1902. model2 = self.build_model_perturb_noise_prob_brown_corr(nUnit=nUnit, nInh=nInh, nInput=nInput,
  1903. con_prob=con_prob, maxval=maxval, ReLUalpha=ReLUalpha,
  1904. pert_ind=pert_ind, pert_which=pert_which,
  1905. seed1=seed1, pert_noisesd=pert_noisesd, tau=tau,
  1906. rank=rank,exp=exp,sample_size=sample_size,
  1907. stateful=stateful,noise_weights=noise_weights,
  1908. noise_vec=noise_vec,
  1909. sync_noise=sync_noise,
  1910. option=option,mat_AB=mat_AB)
  1911. # Copy the weights from the provided model.
  1912. self.model2=model2
  1913. if option.lower()=="running_corr":
  1914. model2.set_weights(model.get_weights())
  1915. elif option.lower()=="cca_values":
  1916. w_model = model.get_weights() # length == 6
  1917. w_model2 = model2.get_weights() # length == 8
  1918. # 2) overwrite the corresponding entries
  1919. for i in range(len(w_model)):
  1920. w_model2[i] = w_model[i]
  1921. # 3) push them back into model2
  1922. model2.set_weights(w_model2)
  1923. model2.reset_states()
  1924. if not stateful:
  1925. predictions = model2.predict(x)
  1926. else:
  1927. for i in range(len(x)):
  1928. xin = tf.stop_gradient(tf.convert_to_tensor(x[i], tf.float32))
  1929. pred = model2.predict_on_batch(xin)
  1930. if not isinstance(pred, (list, tuple)):
  1931. pred = [pred]
  1932. if i == 0:
  1933. outputs_split = [[] for _ in pred]
  1934. for j in range(len(pred)):
  1935. outputs_split[j].append(pred[j])
  1936. # stitch the time segments back together
  1937. predictions = [
  1938. np.concatenate(output_list, axis=1)
  1939. for output_list in outputs_split
  1940. ]
  1941. self.In_ons_temp=In_ons
  1942. return predictions #
  1943. def RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_output(self,nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  1944. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=0.5,noise_weights=None,
  1945. stateful=False,start=0, option="lyapunov",test_mat=None, project_mat=None, update_state=True):
  1946. A_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1947. B_mask = self.build_masks(nUnit, nInh, con_prob, seed=seed1)
  1948. if stateful is False:
  1949. visible = Input(shape=(None,nInput+rank))
  1950. else:
  1951. visible = Input(batch_shape=(sample_size, None, nInput+rank))
  1952. if noise_weights is None:
  1953. self.noise_weights=self.create_noise_weights(nUnit,rank,exp,2)
  1954. print('Random noise weight created')
  1955. else:
  1956. self.noise_weights=noise_weights.copy()
  1957. if option.lower()=="noise_sing":
  1958. rnn_cell = RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_output(
  1959. nUnit,
  1960. output_activation=tf.keras.layers.ReLU(max_value=1000),
  1961. input_activation=lambda x: tf.where(x > 0, x, ReLUalpha * x),
  1962. use_bias=False,
  1963. kernel_initializer=initializers.RandomNormal(mean=0., stddev=1/np.sqrt(nInput), seed=seed1),
  1964. recurrent_initializer=OrthoCustom3(gain=0.5, seed=seed1, nUnit=nUnit, nInh=nInh, conProb=con_prob),
  1965. recurrent_constraint=IEWeightandLim(nInh=nInh, A_mask=A_mask, B_mask=B_mask, maxval=maxval),
  1966. kernel_trainable=True,
  1967. seed=seed1,
  1968. tau=tau,
  1969. noisesd=0.08,
  1970. perturb_ind=pert_ind,
  1971. pert_which=pert_which,
  1972. pert_noisesd=pert_noisesd,
  1973. noise_weights=tf.convert_to_tensor(self.noise_weights),
  1974. )
  1975. # Create the RNN layer with your custom cell and set it to stateless
  1976. rnn_layer = RNN(
  1977. rnn_cell,
  1978. stateful=stateful, # now stateless
  1979. return_sequences=True,
  1980. activity_regularizer=l2(0.1)
  1981. )
  1982. # Call the RNN layer; initial states will be automatically set to zeros
  1983. outputs = rnn_layer(visible)
  1984. # Here, outputs is a list of two tensors.
  1985. hidden_outputs = outputs[0] # shape: (batch, timesteps, 4)
  1986. Jacobian = outputs[1] # shape: (batch, timesteps, 4*2)
  1987. norm_cell=Noise_jacob(nUnit=2, start=start, sing_or_eig=True)
  1988. dummy_input = tf.zeros((sample_size, 1)) # Dummy input if needed (can also pass None in later versions)
  1989. initial_state = norm_cell.get_initial_state(inputs=dummy_input, batch_size=sample_size, dtype=tf.float32)
  1990. Rlayer=RNN(norm_cell,stateful=stateful, return_sequences=True)# (batch_num,timestep,2*nUnits)
  1991. Rmat = Rlayer(Jacobian, initial_state=initial_state) #size(batch, 1, (2*nUnit)^2)
  1992. model = Model(inputs=visible, outputs=[hidden_outputs, Rmat, Jacobian])
  1993. return model
  1994. def run_model_with_jacobian_output_noise(self, trial1, ind1, pert_which, order, pert_noisesd, stop,
  1995. sample_size, trial_num, inputdur, nInput,
  1996. nUnit, nInh, con_prob, maxval, ReLUalpha, seed1, tau, model,
  1997. brown_scale, rank, exp=1, noise_weights=None,stateful=False, start=None,
  1998. option="lyapunov",test_mat=None, batch_size=10, update_state=True,
  1999. lr_AB=[None, None], lr_BA=[None,None],jacob_at_zero=False, project_mat=None):
  2000. # Create input and output arrays using the helper function.
  2001. if stateful is False:
  2002. x, y, In_ons = self.makeInOut_sameint_brown(sample_size, trial_num, inputdur, nInput, order,
  2003. brown_scale=brown_scale, rank=rank, exp=exp)
  2004. else:
  2005. x, y, In_ons, Wbin= self.makeInOut_sameint_brown_stateful(sample_size, trial_num,
  2006. inputdur, nInput,
  2007. order,brown_scale=brown_scale,
  2008. rank=rank, exp=exp,
  2009. batch_num=batch_size)
  2010. if start is None:
  2011. start=In_ons[0,2]
  2012. self.start=start
  2013. # Compute the perturbation indices.
  2014. pert_ind = In_ons[:, [trial1]] + ind1
  2015. # Optionally modify the input if stop is True.
  2016. if stop:
  2017. x, In_ons = self.makeInput(x, In_ons, pert_ind)
  2018. # Build a new model that computes the lyapunov exponents with the provided parameters.
  2019. model2 = self.RNNCustom2FixPerturb_noise_prob_brown_noise_jacobian_output(
  2020. nUnit, nInh, nInput, sample_size, con_prob, maxval, ReLUalpha,
  2021. pert_ind, pert_which, seed1, pert_noisesd, tau, rank,exp=exp,noise_weights=noise_weights,
  2022. stateful=stateful,start=start,option=option,
  2023. test_mat=test_mat,update_state=update_state, project_mat=project_mat
  2024. )
  2025. self.model2=model2
  2026. # transfer the weight from model to model2
  2027. rnn_core_w = model.layers[1].get_weights() # [kernel, recurrent_kernel, bias]
  2028. d2_w = model.layers[3].get_weights() # [dense2_kernel, dense2_bias]
  2029. d3_w = model.layers[4].get_weights() # [dense3_kernel, dense3_bias]
  2030. # 2) Concatenate into the order your custom cell expects:
  2031. # [ core_kernel, core_recurrent, core_bias,
  2032. # dense1_kernel, dense1_bias,
  2033. # dense2_kernel, dense2_bias ]
  2034. new_weights = rnn_core_w + d2_w + d3_w
  2035. # 3) Assign into your custom-cell RNN
  2036. rnn_custom = model2.layers[1] # absolute index of the RNN wrapper
  2037. rnn_custom.set_weights(new_weights)
  2038. #model2.summary()
  2039. model2.reset_states()
  2040. # 3) Compare them
  2041. fun_w_all = rnn_core_w + d2_w + d3_w
  2042. # 2) Pull out the custom‐cell RNN weights
  2043. rnn_custom_w = model2.layers[1].get_weights()
  2044. # 3) Compare them
  2045. all_same = True
  2046. for idx, (wf, wc) in enumerate(zip(fun_w_all, rnn_custom_w)):
  2047. same = np.allclose(wf, wc, atol=1e-6)
  2048. print(f"Weight #{idx}: shape {wf.shape} — match? {same}")
  2049. if not same:
  2050. all_same = False
  2051. print("\nAll weights identical:", all_same)
  2052. # Run the prediction.
  2053. if stateful is False:
  2054. predictions = model2.predict(x) #(output, Rmat, A_activity, B_activity
  2055. else:
  2056. for i in range(len(x)):
  2057. print(f"prediction: {i} out of {len(x)}")
  2058. xin = tf.convert_to_tensor(x[i], dtype=tf.float32)
  2059. xin=tf.stop_gradient(xin)
  2060. pred = model2.predict_on_batch(xin) # pred = (output, Rmat, A_activity, B_activity)
  2061. if not isinstance(pred, (list, tuple)):
  2062. pred = [pred] #1, sample_size, time, output_dim
  2063. if i==0:
  2064. outputs_split = [[] for _ in range(len(pred))]
  2065. for j in range(len(pred)):
  2066. outputs_split[j].append(pred[j])
  2067. # Concatenate each type of output across time (axis=1)
  2068. predictions = [np.concatenate(output_list, axis=1) for output_list in outputs_split]
  2069. self.In_ons_temp=In_ons
  2070. return predictions #

decode_analyze.py at commit 101fb44, no license · at the source

Overview

Authors: Hiroto Imamura1, Fumiya Imamura1, Reiko Hira1, Yoshikazu Isomura1, Riichiro Hira1
  1. Department of Physiology and Cell Biology, Graduate School of Medical and Dental Sciences, Institute of Science Tokyo, Tokyo, Japan
Institutions: Institute of Science Tokyo (Japan)
Journal: Nature communications, volume 17, issue 1, article 5174
Dates: received 2 September 2025; accepted 21 May 2026; published online 11 June 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41467-026-73999-w · PMID 42277018 · PMCID PMC13260406 · OpenAlex W4413616999
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: mouse (organism), systems (subfield)
Methods: Spectral & time-frequency, Statistics, Smoothing, state filtering, decompositions, Machine learning, Connectivity, Preprocessing, fMRI & imaging, Single-unit activity, calcium imaging, Physiology & signal measures
Keywords: Cognitive neuroscience, Computational neuroscience, Neural circuits
MeSH: Frontal Lobe*, Motor Cortex*, Nerve Net*, Parietal Lobe*, Time Perception*, Animals, Male, Mice, Mice, Inbred C57BL, Models, Neurological, Neurons, Recurrent Neural Networks (* major topic)
Topic: Constraint Satisfaction and Optimization (Computer Networks and Communications, Computer Science), according to OpenAlex
Funding: Nakatani Foundation for Advancement of Measuring Technologies in Biomedical Engineering; Takeda Science Foundation
Citations: cited by 3 papers (Europe PMC); 78 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

Its files are read in the Code ↔ Paper reader above, with 4 matches between paragraphs and lines of code.

hiroto726/TwinRNN

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 101fb44593cd930112c1058974ada0c461813a3d, 30 April 2026
Languages: Python (13), Jupyter (1)
Size: 26 files, 14 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, environment (Dockerfile, environment.yml, requirements.txt), 1 notebook
Not found: license file, CITATION.cff, tests, continuous integration, documentation
Tools: Keras (8 files), TensorFlow (8 files), NumPy (7 files), Matplotlib (2 files), scikit-learn (2 files), SciPy (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
15 files

Zenodo 19907436

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Keras (8 files), TensorFlow (8 files), NumPy (7 files), Matplotlib (2 files), scikit-learn (2 files), SciPy (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
15 files
At the source:

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41467-026-73999-w.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 28 scripts, each with its path and the digest of its content;
  • 4 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41467-026-73999-w.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 3 keywords, 12 MeSH terms, 2 funders, 74 references.

Cite

This paper

Imamura, H., Imamura, F., Hira, R., Isomura, Y., & Hira, R. (2026). Independence and coherence in temporal sequence computation across the fronto-parietal network. Nature communications, 17(1), 5174. https://doi.org/10.1038/s41467-026-73999-w

BibTeX

@article{imamura2026independence,
author = {Imamura, Hiroto and Imamura, Fumiya and Hira, Reiko and Isomura, Yoshikazu and Hira, Riichiro},
title = {{Independence and coherence in temporal sequence computation across the fronto-parietal network}},
journal = {Nature communications},
year = {2026},
month = jun,
volume = {17},
number = {1},
pages = {5174},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-73999-w},
url = {https://doi.org/10.1038/s41467-026-73999-w},
pmid = {42277018},
pmcid = {PMC13260406}
}

RIS

TY - JOUR
AU - Imamura, Hiroto
AU - Imamura, Fumiya
AU - Hira, Reiko
AU - Isomura, Yoshikazu
AU - Hira, Riichiro
TI - Independence and coherence in temporal sequence computation across the fronto-parietal network
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/06/11
VL - 17
IS - 1
SP - 5174
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-73999-w
UR - https://doi.org/10.1038/s41467-026-73999-w
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-73999-w",
"type": "article-journal",
"title": "Independence and coherence in temporal sequence computation across the fronto-parietal network",
"container-title": "Nature communications",
"author": [
{
"family": "Imamura",
"given": "Hiroto"
},
{
"family": "Imamura",
"given": "Fumiya"
},
{
"family": "Hira",
"given": "Reiko"
},
{
"family": "Isomura",
"given": "Yoshikazu"
},
{
"family": "Hira",
"given": "Riichiro"
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "5174",
"DOI": "10.1038/s41467-026-73999-w",
"PMID": "42277018",
"PMCID": "PMC13260406",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-73999-w",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
11
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.7554/elife.105213 [code]
Mesoscale functional architecture in medial posterior parietal cortex.
Journal: eLife
In common: mouse, 12 references, author Riichiro Hira
[2] doi:10.1126/sciadv.aed6417 [code]
Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior.
Journal: Science advances
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, systems, mouse, 10 references
[3] doi:10.1038/s41467-026-71725-0 [code]
Interactions across hemispheres in prefrontal cortex reflect global cognitive processing.
Journal: Nature communications
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, 8 references
[4] doi:10.1016/j.neuron.2026.07.016 [code]
Inferring brain-wide interactions using data-constrained recurrent neural network models.
Journal: Neuron
In common: Matplotlib, NumPy, systems, mouse, 8 references
[5] doi:10.1038/s41586-026-10528-1 [code]
A critical initialization for biological neural networks.
Journal: Nature
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, mouse, 5 references
[6] doi:10.1371/journal.pbio.3003915 [code]
Noise-invariant representations of sound emerge along the canonical cortical hierarchy.
Journal: PLoS biology
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, mouse, 4 references
[7] doi:10.1371/journal.pbio.3003831 [code]
Disinhibitory signaling enables flexible coding of top-down information in cortical networks.
Journal: PLoS biology
In common: TensorFlow, scikit-learn, SciPy, 2 other tools, systems, mouse, 2 references
[8] doi: [code]
Real-time closed-loop feedback system for mouse mesoscale cortical signal and movement control
Journal: eLife
In common: Keras, TensorFlow, scikit-learn, 3 other tools, mouse, 1 reference
[9] doi:10.7554/elife.109240 [code]
Neural activity profiles reveal overlapping, intermingled subpopulations spanning area borders in mouse sensorimotor cortex.
Journal: eLife
In common: systems, mouse, 5 references
[10] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: Keras, TensorFlow, scikit-learn, 3 other tools, mouse, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.