Linear & Non-Linear Systems in Discrete Time (DT)

Linear & Non-Linear Systems in Discrete Time (DT)#

Warning

This notebook is under construction.

import numpy as np
import matplotlib.pyplot as plt

plt.style.use('seaborn-v0_8-whitegrid')
plt.rcParams['axes.grid'] = True
plt.rcParams['legend.frameon'] = True
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 1
----> 1 import numpy as np
      2 import matplotlib.pyplot as plt
      4 plt.style.use('seaborn-v0_8-whitegrid')

ModuleNotFoundError: No module named 'numpy'

Amplitude properties:#

  1. Linearity

  2. Stability

  3. Invertibility

Time properties:#

  1. Time invariance

  2. Memory

  3. Causality

Superposition Principle#

  • Law of additivity

  • Law of homogeneity

# Create a time array and input signals
t = np.linspace(0, 2 * np.pi, 100)
x1 = np.sin(t)
x2 = np.sin(2 * t)

# Define the system output (for example, identity system where output = input)
y1 = x1
y2 = x2
y_sum = y1 + y2  # Additivity
y_scaled = 2 * x1  # Homogeneity (scaling)

# Plot the inputs and outputs
fig, axs = plt.subplots(2, 1, figsize=(8, 6))

# Additivity
axs[0].plot(t, y1, label='y1[n] = sin(t)', linestyle='--')
axs[0].plot(t, y2, label='y2[n] = sin(2t)', linestyle='--')
axs[0].plot(t, y_sum, label='y1[n] + y2[n]', color='r', linewidth=2)
axs[0].set_title('Additivity: y[n] = y1[n] + y2[n]')
axs[0].legend()

# Homogeneity
axs[1].plot(t, y1, label='y1[n] = sin(t)', linestyle='--')
axs[1].plot(t, y_scaled, label='2 * y1[n]', color='g', linewidth=2)
axs[1].set_title('Homogeneity: y[n] = 2 * y1[n]')
axs[1].legend()

plt.tight_layout()
plt.show()
../_images/c4dfc4c6d89f8ad189c9e6766f722e416efae6c22faaca3df27210dd23aed1ea.png
from scipy.signal import butter, lfilter

# Stable system: Low-pass filter
def butter_lowpass(cutoff, fs, order=5):
    nyquist = 0.5 * fs
    normal_cutoff = cutoff / nyquist
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

# Filter a signal using the low-pass filter
fs = 1000  # Sampling frequency
cutoff = 50  # Cutoff frequency of the filter
order = 6
b, a = butter_lowpass(cutoff, fs, order)

# Create a bounded input signal (sine wave)
t = np.linspace(0, 1.0, fs)
x_bounded = np.sin(2 * np.pi * 10 * t)  # Sine wave at 10 Hz

# Apply stable low-pass filter
y_stable = lfilter(b, a, x_bounded)

# Unstable system: Exponential growth (e.g., unstable feedback system)
y_unstable = np.exp(0.2 * t) * np.sin(2 * np.pi * 10 * t)  # Unstable sine signal

# Plot the stable vs unstable system
plt.figure(figsize=(8, 4))
plt.plot(t, x_bounded, label='Input Signal (Sine)', linestyle='--')
plt.plot(t, y_stable, label='Stable System Output (Low-pass)', color='g')
plt.plot(t, y_unstable, label='Unstable System Output (Exponential)', color='r')
plt.title('Stability: Bounded Input -> Bounded Output (Stable) vs Unbounded Output (Unstable)')
plt.legend()
plt.show()
../_images/faba3e88eef6cf85105ac55532173e0f064657d387c5840de7369370a87fcfd7.png
# Invertible system: Delay by 5 samples
delay_amount = 5
x_input = np.sin(t)
x_delayed = np.roll(x_input, delay_amount)  # Delayed signal

# Non-invertible system: Sum of inputs (loss of information)
y_non_invertible = x_input + x_delayed  # This is not invertible

# Plot both systems
plt.figure(figsize=(8, 4))
plt.plot(t, x_input, label='Original Input Signal', linestyle='--')
plt.plot(t, x_delayed, label=f'Delayed Input (Invertible System)', color='g')
plt.plot(t, y_non_invertible, label='Non-Invertible System (Sum)', color='r')
plt.title('Invertibility: Delay System vs Non-Invertible System (Sum)')
plt.legend()
plt.show()
../_images/a5e8d541793c19b3c277b7544545bd6e8077b0b456629bb4504fab3c31d64c8e.png
# Create a signal
x = np.sin(t)

# Apply time shift (e.g., shift by 20 samples)
shift_amount = 20
x_shifted = np.roll(x, shift_amount)

# Plot the original and shifted signals
plt.figure(figsize=(8, 4))
plt.plot(t, x, label='Original Signal')
plt.plot(t, x_shifted, label=f'Shifted Signal by {shift_amount} samples', linestyle='--')
plt.title('Time Invariance: Shifted Signal')
plt.legend()
plt.show()
../_images/acd59fae5e774a04853f83606c7e239f173843a82c0e8507926ede78a7493c2c.png
from scipy.signal import lfilter

# Create an example input signal (impulse response)
x_impulse = np.zeros(100)
x_impulse[50] = 1  # Impulse at the center

# Define a simple FIR filter (e.g., moving average filter)
filter_coeffs = np.ones(5) / 5  # Average over the last 5 samples
y_fir = lfilter(filter_coeffs, 1, x_impulse)

# Plot the impulse response of the causal system
plt.figure(figsize=(8, 4))
plt.plot(x_impulse, label='Input Signal (Impulse)')
plt.plot(y_fir, label='Output (FIR Filter)', linestyle='--')
plt.title('Causality: FIR Filter Output')
plt.legend()
plt.show()
../_images/4e829e354de91d03697d9a8ba7bab8ae93425b7795b02c1ebef2ca124b796ad4.png
# Create a signal
x_signal = np.sin(t)

# Memoryless system (just scaling the input)
y_memoryless = 2 * x_signal

# System with memory (simple moving average filter)
y_with_memory = lfilter(np.ones(5)/5, 1, x_signal)

# Plot both systems
plt.figure(figsize=(8, 4))
plt.plot(t, x_signal, label='Input Signal')
plt.plot(t, y_memoryless, label='Memoryless System: 2 * Input', linestyle='--')
plt.plot(t, y_with_memory, label='System with Memory (Moving Average)', linestyle='-.')
plt.title('Memory vs. Memoryless Systems')
plt.legend()
plt.show()
../_images/247bd9eb9afb112e5105abe1c725e9faa194e3b99b9dfb06b34b4019cbb488f7.png
# Law of Additivity test for linearity

import numpy as np

# Hypothetical audio processing system function
def audio_processing_system(input_signal):
    # For demonstration, let's assume the system just doubles the input signal
    # A real system might have more complex processing
    output_signal = np.cos(input_signal)
    return output_signal

# Define two input signals
input1 = np.array([1, 2, 3, 4, 5])  # Example input signal A
input2 = np.array([5, 4, 3, 2, 1])  # Example input signal B

# Process inputs individually
output1 = audio_processing_system(input1)
output2 = audio_processing_system(input2)

# Combine inputs and process combined input
combined_input = input1 + input2
combined_output = audio_processing_system(combined_input)

# Sum individual outputs
sum_of_outputs = output1 + output2

# Compare combined output with sum of individual outputs
if np.array_equal(combined_output, sum_of_outputs):
    print("The system follows the law of additivity and is likely linear.")
else:
    print("The system does not follow the law of additivity and is likely non-linear.")
The system does not follow the law of additivity and is likely non-linear.
# Law of Homogeneity test for linearity

# Hypothetical audio processing system function
def audio_processing_system(input_signal):
    # For demonstration, let's assume the system just doubles the input signal
    # A real system might have more complex processing
    output_signal = 2 * input_signal + 1
    return output_signal

# Define an input signal
input_signal = np.array([1, 2, 3, 4, 5])  # Example input signal

# Define a scaling factor
scaling_factor = 3

# Process the original input signal
original_output = audio_processing_system(input_signal)

# Scale the input signal and process the scaled input
scaled_input = scaling_factor * input_signal
scaled_output = audio_processing_system(scaled_input)

# Check if the scaled output is the scaling factor times the original output
expected_scaled_output = scaling_factor * original_output

# Compare scaled output with expected scaled output
if np.array_equal(scaled_output, expected_scaled_output):
    print("The system follows the law of homogeneity and is likely linear.")
else:
    print("The system does not follow the law of homogeneity and is likely non-linear.")
The system does not follow the law of homogeneity and is likely non-linear.