Direct Modulation Index#
The following example shows how to apply the dmi to estimate PAC.
import numpy as np
import matplotlib
matplotlib.use("Qt5agg")
import matplotlib.pyplot as plt
import finn.cfc.pac as pac
def generate_high_frequency_signal(n, frequency_sampling, frequency_within_bursts, random_noise_strength,
                             offset, burst_count, burst_length):
   signal = np.random.normal(0, 1, n) * random_noise_strength
   for burst_start in np.arange(offset, n, n/burst_count):
   burst_end = burst_start + (burst_length/2)
   signal[int(burst_start):int(burst_end)] =  np.sin(2 * np.pi * frequency_within_bursts * np.arange(0, (int(burst_end) - int(burst_start))) / frequency_sampling)
   return signal
def visualize(scores, best_fits, amplitude_signals, frequencies_between_bursts, tgt_frequency_between_bursts, high_freq_frame_offsets):
   if(len(scores) < 5):
   (_, axes) = plt.subplots(2, 2)
   elif(len(scores) < 10):
   (_, axes) = plt.subplots(3, 3)
   elif(len(scores) < 17):
   (_, axes) = plt.subplots(4, 4)
   elif(len(scores) < 26):
   (_, axes) = plt.subplots(5, 5)
   else:
   raise NotImplementedError
   axes = np.reshape(axes, -1)
   for (ax_idx, ax) in enumerate(axes):
   ax.plot(np.arange(0, 1, 1/len(amplitude_signals[ax_idx])), amplitude_signals[ax_idx], label = "original data")
   ax.plot(np.arange(0, 1, 1/len(amplitude_signals[ax_idx])), best_fits[ax_idx], label = "fitted curve")
   if (frequencies_between_bursts[int(ax_idx % len(high_freq_frame_offsets))] < tgt_frequency_between_bursts):
      ax.set_title("No PAC, low frequency too low. PAC score: %2.2f" % (scores[ax_idx],))
   elif (frequencies_between_bursts[int(ax_idx % len(high_freq_frame_offsets))] > tgt_frequency_between_bursts):
      ax.set_title("No PAC, low frequency too high. PAC score: %2.2f" % (scores[ax_idx],))
   else:
      ax.set_title("Good PAC, low frequency matches. PAC score: %2.2f" % (scores[ax_idx],))
def main():
   #Configure sample data
   data_range = np.arange(0, 10000)
   frequency_sampling = 1000
   frequencies_between_bursts = [5, 10, 15]
   tgt_frequency_between_bursts = 10
   frequency_within_bursts = 200
   high_freq_frame_offsets = [0, 20, 40]
   #Configure noise data
   random_noise_strength = 1
   #Generate sample data
   burst_length = frequency_sampling / tgt_frequency_between_bursts
   burst_count = len(data_range) / frequency_sampling * tgt_frequency_between_bursts
   high_freq_signals = [generate_high_frequency_signal(len(data_range), frequency_sampling, frequency_within_bursts,
                                                  random_noise_strength, high_freq_frame_offset, burst_count, burst_length) for high_freq_frame_offset in high_freq_frame_offsets]
   low_freq_signals = [np.sin(2 * np.pi * frequency_between_bursts * data_range / frequency_sampling) for frequency_between_bursts in frequencies_between_bursts]
   scores = list(); best_fits = list(); amplitude_signals = list()
   for high_freq_signal in high_freq_signals:
   for low_freq_signal in low_freq_signals:
      tmp = pac.run_dmi(low_freq_signal, high_freq_signal, phase_window_half_size = 20, phase_step_width = 5)
      scores.append(tmp[0]); best_fits.append(tmp[1]); amplitude_signals.append(tmp[2])
   #visualization
   visualize(scores, best_fits, amplitude_signals, frequencies_between_bursts, tgt_frequency_between_bursts, high_freq_frame_offsets)
   plt.tight_layout()
   plt.legend()
   figManager = plt.get_current_fig_manager()
   figManager.window.showMaximized()
   plt.show(block = True)
main()
Using the direct modulation index, PAC has been detected only where there was a match between the high frequency component and the low frequency component:
