Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@
* [Simplex](linear_programming/simplex.py)

## [Machine Learning](machine_learning)
* [Multi-Armed Bandits ](machine_learning/Multi-Armed%20Bandits%20.py)
* [Apriori Algorithm](machine_learning/apriori_algorithm.py)
* [Astar](machine_learning/astar.py)
* [Automatic Differentiation](machine_learning/automatic_differentiation.py)
Expand All @@ -703,6 +704,7 @@
* [K Means Clust](machine_learning/k_means_clust.py)
* [K Medoids](machine_learning/k_medoids.py)
* [K Nearest Neighbors](machine_learning/k_nearest_neighbors.py)
* [Kernel Svm](machine_learning/kernel_svm.ipynb)
* [Linear Discriminant Analysis](machine_learning/linear_discriminant_analysis.py)
* [Linear Regression](machine_learning/linear_regression.py)
* [Linear Regression Vectorized](machine_learning/linear_regression_vectorized.py)
Expand All @@ -712,14 +714,17 @@
* [Loss Functions](machine_learning/loss_functions.py)
* Lstm
* [Lstm Prediction](machine_learning/lstm/lstm_prediction.py)
* [Mean Shift](machine_learning/mean_shift.py)
* [Mfcc](machine_learning/mfcc.py)
* [Mini Batch Gradient Descent](machine_learning/mini_batch_gradient_descent.py)
* [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py)
* [Naive Bayes Text Classification](machine_learning/naive_bayes_text_classification.py)
* [Polynomial Regression](machine_learning/polynomial_regression.py)
* [Principle Component Analysis](machine_learning/principle_component_analysis.py)
* [Q Learning](machine_learning/q_learning.py)
* [Random Forest Classifier](machine_learning/random_forest_classifier.py)
* [Random Forest Regressor](machine_learning/random_forest_regressor.py)
* [Rmsprop](machine_learning/rmsprop.py)
* [Scoring Functions](machine_learning/scoring_functions.py)
* [Self Organizing Map](machine_learning/self_organizing_map.py)
* [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py)
Expand All @@ -736,6 +741,7 @@
* [Arc Length](maths/arc_length.py)
* [Area](maths/area.py)
* [Area Under Curve](maths/area_under_curve.py)
* [Autocorrelation](maths/autocorrelation.py)
* [Average Absolute Deviation](maths/average_absolute_deviation.py)
* [Average Mean](maths/average_mean.py)
* [Average Median](maths/average_median.py)
Expand Down Expand Up @@ -781,6 +787,7 @@
* [Fibonacci](maths/fibonacci.py)
* [Find Max](maths/find_max.py)
* [Find Min](maths/find_min.py)
* [First Fundamental Form](maths/first_fundamental_form.py)
* [Floor](maths/floor.py)
* [Gamma](maths/gamma.py)
* [Gaussian](maths/gaussian.py)
Expand Down Expand Up @@ -843,6 +850,7 @@
* [Square Root](maths/numerical_analysis/square_root.py)
* [Weierstrass Method](maths/numerical_analysis/weierstrass_method.py)
* [Odd Sieve](maths/odd_sieve.py)
* [Padovan Sequence](maths/padovan_sequence.py)
* [Pell Number](maths/pell_number.py)
* [Perfect Cube](maths/perfect_cube.py)
* [Perfect Number](maths/perfect_number.py)
Expand Down Expand Up @@ -872,6 +880,8 @@
* [Reverse Factorial Recursive](maths/reverse_factorial_recursive.py)
* [Segmented Sieve](maths/segmented_sieve.py)
* Series
* [Alternate Harmonic Series](maths/series/alternate_harmonic_series.py)
* [Alternating Harmonic Series](maths/series/alternating_harmonic_series.py)
* [Arithmetic](maths/series/arithmetic.py)
* [Geometric](maths/series/geometric.py)
* [Geometric Series](maths/series/geometric_series.py)
Expand Down Expand Up @@ -911,6 +921,7 @@
* [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py)
* [Pronic Number](maths/special_numbers/pronic_number.py)
* [Proth Number](maths/special_numbers/proth_number.py)
* [Spy Number](maths/special_numbers/spy_number.py)
* [Triangular Numbers](maths/special_numbers/triangular_numbers.py)
* [Trimorphic Number](maths/special_numbers/trimorphic_number.py)
* [Ugly Numbers](maths/special_numbers/ugly_numbers.py)
Expand Down
241 changes: 241 additions & 0 deletions machine_learning/Multi-Armed Bandits .py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import numpy as np

Check failure on line 1 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (N999)

machine_learning/Multi-Armed Bandits .py:1:1: N999 Invalid module name: 'Multi-Armed Bandits '
import matplotlib.pyplot as plt
from abc import ABC, abstractmethod

Check failure on line 3 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

machine_learning/Multi-Armed Bandits .py:1:1: I001 Import block is un-sorted or un-formatted help: Organize imports


class BanditAlgorithm(ABC):
"""Base class for bandit algorithms"""

def __init__(self, n_arms):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

self.n_arms = n_arms
self.reset()

def reset(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: reset. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function reset

self.counts = np.zeros(self.n_arms)
self.rewards = np.zeros(self.n_arms)
self.t = 0

@abstractmethod
def select_arm(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: select_arm. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function select_arm

pass

def update(self, arm, reward):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: update. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: arm

Please provide type hint for the parameter: reward

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function update

self.t += 1
self.counts[arm] += 1
self.rewards[arm] += reward


class EpsilonGreedy(BanditAlgorithm):
"""Epsilon-Greedy Algorithm"""

def __init__(self, n_arms, epsilon=0.1):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

Please provide type hint for the parameter: epsilon

super().__init__(n_arms)
self.epsilon = epsilon

def select_arm(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: select_arm. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function select_arm

if np.random.random() < self.epsilon:

Check failure on line 36 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:36:12: NPY002 Replace legacy `np.random.random` call with `np.random.Generator`
# Explore: random arm
return np.random.randint(self.n_arms)

Check failure on line 38 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:38:20: NPY002 Replace legacy `np.random.randint` call with `np.random.Generator`
else:
# Exploit: best arm so far
avg_rewards = np.divide(
self.rewards,
self.counts,
out=np.zeros_like(self.rewards),
where=self.counts != 0,
)
return np.argmax(avg_rewards)


class UCB(BanditAlgorithm):
"""Upper Confidence Bound Algorithm"""

def __init__(self, n_arms, c=2.0):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

Please provide type hint for the parameter: c

Please provide descriptive name for the parameter: c

super().__init__(n_arms)
self.c = c

def select_arm(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: select_arm. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function select_arm

# If any arm hasn't been tried, try it
if 0 in self.counts:
return np.where(self.counts == 0)[0][0]

# Calculate UCB values
avg_rewards = self.rewards / self.counts
confidence = self.c * np.sqrt(np.log(self.t) / self.counts)
ucb_values = avg_rewards + confidence

return np.argmax(ucb_values)


class ThompsonSampling(BanditAlgorithm):
"""Thompson Sampling (Beta-Bernoulli)"""

def __init__(self, n_arms):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

super().__init__(n_arms)
self.alpha = np.ones(n_arms) # Prior successes
self.beta = np.ones(n_arms) # Prior failures

def select_arm(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: select_arm. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function select_arm

# Sample from Beta distribution for each arm
samples = np.random.beta(self.alpha, self.beta)

Check failure on line 80 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:80:19: NPY002 Replace legacy `np.random.beta` call with `np.random.Generator`
return np.argmax(samples)

def update(self, arm, reward):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: update. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: arm

Please provide type hint for the parameter: reward

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function update

super().update(arm, reward)
# Update Beta parameters
if reward > 0:
self.alpha[arm] += 1
else:
self.beta[arm] += 1


class GradientBandit(BanditAlgorithm):
"""Gradient Bandit Algorithm"""

def __init__(self, n_arms, alpha=0.1):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

Please provide type hint for the parameter: alpha

super().__init__(n_arms)
self.alpha = alpha
self.preferences = np.zeros(n_arms)
self.avg_reward = 0

def select_arm(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: select_arm. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function select_arm

# Softmax to get probabilities
exp_prefs = np.exp(self.preferences - np.max(self.preferences))
probs = exp_prefs / np.sum(exp_prefs)
return np.random.choice(self.n_arms, p=probs)

Check failure on line 105 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:105:16: NPY002 Replace legacy `np.random.choice` call with `np.random.Generator`

def update(self, arm, reward):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: update. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: arm

Please provide type hint for the parameter: reward

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function update

super().update(arm, reward)

# Update average reward
self.avg_reward += (reward - self.avg_reward) / self.t

# Get action probabilities
exp_prefs = np.exp(self.preferences - np.max(self.preferences))
probs = exp_prefs / np.sum(exp_prefs)

# Update preferences
for a in range(self.n_arms):
if a == arm:
self.preferences[a] += (
self.alpha * (reward - self.avg_reward) * (1 - probs[a])
)
else:
self.preferences[a] -= (
self.alpha * (reward - self.avg_reward) * probs[a]
)


# Testbed for comparing algorithms
class BanditTestbed:
"""Environment for testing bandit algorithms"""

def __init__(self, n_arms=10, true_rewards=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: n_arms

Please provide type hint for the parameter: true_rewards

self.n_arms = n_arms
if true_rewards is None:
self.true_rewards = np.random.normal(0, 1, n_arms)

Check failure on line 136 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:136:33: NPY002 Replace legacy `np.random.normal` call with `np.random.Generator`
else:
self.true_rewards = true_rewards
self.optimal_arm = np.argmax(self.true_rewards)

def get_reward(self, arm):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: get_reward. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: arm

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function get_reward

"""Get noisy reward for pulling an arm"""
return np.random.normal(self.true_rewards[arm], 1)

Check failure on line 143 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (NPY002)

machine_learning/Multi-Armed Bandits .py:143:16: NPY002 Replace legacy `np.random.normal` call with `np.random.Generator`

def run_experiment(self, algorithm, n_steps=1000):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: run_experiment. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: algorithm

Please provide type hint for the parameter: n_steps

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function run_experiment

"""Run bandit algorithm for n_steps"""
algorithm.reset()
rewards = []
optimal_actions = []

for _ in range(n_steps):
arm = algorithm.select_arm()
reward = self.get_reward(arm)
algorithm.update(arm, reward)

rewards.append(reward)
optimal_actions.append(1 if arm == self.optimal_arm else 0)

return np.array(rewards), np.array(optimal_actions)


# Example usage and comparison
def compare_algorithms():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: compare_algorithms. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file machine_learning/Multi-Armed Bandits .py, please provide doctest for the function compare_algorithms

"""Compare different bandit algorithms"""

# Create testbed
testbed = BanditTestbed(n_arms=10)

# Initialize algorithms
algorithms = {
"ε-greedy (0.1)": EpsilonGreedy(10, epsilon=0.1),
"ε-greedy (0.01)": EpsilonGreedy(10, epsilon=0.01),
"UCB (c=2)": UCB(10, c=2),
"Thompson Sampling": ThompsonSampling(10),
"Gradient Bandit": GradientBandit(10, alpha=0.1),
}

n_steps = 2000
n_runs = 100

results = {}

for name, algorithm in algorithms.items():
print(f"Running {name}...")
avg_rewards = np.zeros(n_steps)
optimal_actions = np.zeros(n_steps)

for run in range(n_runs):

Check failure on line 188 in machine_learning/Multi-Armed Bandits .py

View workflow job for this annotation

GitHub Actions / ruff

ruff (B007)

machine_learning/Multi-Armed Bandits .py:188:13: B007 Loop control variable `run` not used within loop body help: Rename unused `run` to `_run`
rewards, optimal = testbed.run_experiment(algorithm, n_steps)
avg_rewards += rewards
optimal_actions += optimal

avg_rewards /= n_runs
optimal_actions /= n_runs

results[name] = {"rewards": avg_rewards, "optimal_actions": optimal_actions}

# Plot results
plt.figure(figsize=(15, 5))

# Average reward over time
plt.subplot(1, 2, 1)
for name, result in results.items():
plt.plot(np.cumsum(result["rewards"]) / np.arange(1, n_steps + 1), label=name)
plt.xlabel("Steps")
plt.ylabel("Average Reward")
plt.title("Average Reward vs Steps")
plt.legend()
plt.grid(True)

# Percentage of optimal actions
plt.subplot(1, 2, 2)
for name, result in results.items():
plt.plot(
np.cumsum(result["optimal_actions"]) / np.arange(1, n_steps + 1) * 100,
label=name,
)
plt.xlabel("Steps")
plt.ylabel("% Optimal Action")
plt.title("Optimal Action Selection vs Steps")
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

return results


# Run the comparison
if __name__ == "__main__":
results = compare_algorithms()

# Print final performance
print("\nFinal Performance (last 100 steps):")
for name, result in results.items():
avg_reward = np.mean(result["rewards"][-100:])
optimal_pct = np.mean(result["optimal_actions"][-100:]) * 100
print(
f"{name:20s}: Avg Reward = {avg_reward:.3f}, Optimal = {optimal_pct:.1f}%"
)
Loading
Loading