Initial commit: OptimizR - High-performance optimization algorithms in Rust with Python bindings

This commit is contained in:
Melvin Avarez
2025-12-03 18:16:48 +01:00
commit 923d27e87b
31 changed files with 7466 additions and 0 deletions
+370
View File
@@ -0,0 +1,370 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "c263c5be",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from optimizr import HMM\n",
"\n",
"# Set random seed for reproducibility\n",
"np.random.seed(42)\n",
"\n",
"print(\"OptimizR HMM Module Loaded Successfully!\")"
]
},
{
"cell_type": "markdown",
"id": "d2d18086",
"metadata": {},
"source": [
"## Example 1: Market Regime Detection\n",
"\n",
"We'll model financial returns with 3 hidden states:\n",
"- **State 0:** Bull Market (high mean, low volatility)\n",
"- **State 1:** Bear Market (negative mean, high volatility)\n",
"- **State 2:** Sideways/Neutral (zero mean, medium volatility)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f6141fe5",
"metadata": {},
"outputs": [],
"source": [
"def generate_regime_data(n_samples=500, seed=42):\n",
" \"\"\"\n",
" Generate synthetic market returns with 3 regimes.\n",
" \"\"\"\n",
" np.random.seed(seed)\n",
" \n",
" # Define true regime parameters\n",
" true_means = np.array([0.08, -0.06, 0.01]) # Bull, Bear, Sideways\n",
" true_stds = np.array([0.02, 0.05, 0.03]) # Volatilities\n",
" \n",
" # Transition matrix (tend to stay in same regime)\n",
" transition_matrix = np.array([\n",
" [0.85, 0.10, 0.05], # Bull -> Bull, Bear, Sideways\n",
" [0.10, 0.80, 0.10], # Bear -> ...\n",
" [0.15, 0.15, 0.70] # Sideways -> ...\n",
" ])\n",
" \n",
" # Generate state sequence\n",
" true_states = [0] # Start in bull market\n",
" for _ in range(n_samples - 1):\n",
" current_state = true_states[-1]\n",
" next_state = np.random.choice(3, p=transition_matrix[current_state])\n",
" true_states.append(next_state)\n",
" \n",
" true_states = np.array(true_states)\n",
" \n",
" # Generate observations\n",
" returns = np.zeros(n_samples)\n",
" for t in range(n_samples):\n",
" state = true_states[t]\n",
" returns[t] = np.random.normal(true_means[state], true_stds[state])\n",
" \n",
" return returns, true_states, true_means, true_stds\n",
"\n",
"# Generate data\n",
"returns, true_states, true_means, true_stds = generate_regime_data()\n",
"\n",
"print(f\"Generated {len(returns)} return observations\")\n",
"print(f\"True means: {true_means}\")\n",
"print(f\"True stds: {true_stds}\")\n",
"print(f\"State distribution: {np.bincount(true_states)}\")"
]
},
{
"cell_type": "markdown",
"id": "b172f5ef",
"metadata": {},
"source": [
"### Visualize the Generated Data"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "23ca412a",
"metadata": {},
"outputs": [],
"source": [
"fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True)\n",
"\n",
"# Plot returns with color-coded regimes\n",
"colors = ['green', 'red', 'gray']\n",
"regime_names = ['Bull', 'Bear', 'Sideways']\n",
"\n",
"for state in range(3):\n",
" mask = true_states == state\n",
" axes[0].scatter(np.where(mask)[0], returns[mask], \n",
" c=colors[state], label=regime_names[state], alpha=0.6, s=20)\n",
"\n",
"axes[0].axhline(y=0, color='black', linestyle='--', alpha=0.3)\n",
"axes[0].set_ylabel('Returns', fontsize=12)\n",
"axes[0].set_title('Synthetic Market Returns (Color = True Regime)', fontsize=14, fontweight='bold')\n",
"axes[0].legend()\n",
"axes[0].grid(alpha=0.3)\n",
"\n",
"# Plot cumulative returns\n",
"cumulative = np.cumsum(returns)\n",
"axes[1].plot(cumulative, linewidth=2, color='blue')\n",
"axes[1].set_xlabel('Time', fontsize=12)\n",
"axes[1].set_ylabel('Cumulative Return', fontsize=12)\n",
"axes[1].set_title('Cumulative Returns', fontsize=14, fontweight='bold')\n",
"axes[1].grid(alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "f4139a04",
"metadata": {},
"source": [
"## Fit the HMM Model\n",
"\n",
"Now we'll use the **Baum-Welch algorithm** to learn the parameters from data."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a2944703",
"metadata": {},
"outputs": [],
"source": [
"# Create and fit HMM\n",
"hmm = HMM(n_states=3, random_state=42)\n",
"\n",
"print(\"Fitting HMM with Baum-Welch algorithm...\")\n",
"hmm.fit(returns, n_iterations=100, tolerance=1e-6)\n",
"\n",
"print(\"\\nLearned Parameters:\")\n",
"print(f\"Transition Matrix:\\n{hmm.transition_matrix_}\")\n",
"print(f\"\\nEmission Means: {hmm.emission_means_}\")\n",
"print(f\"Emission Stds: {hmm.emission_stds_}\")"
]
},
{
"cell_type": "markdown",
"id": "b66fd9db",
"metadata": {},
"source": [
"## Decode States with Viterbi Algorithm"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9fb3e502",
"metadata": {},
"outputs": [],
"source": [
"# Predict states using Viterbi\n",
"predicted_states = hmm.predict(returns)\n",
"\n",
"print(f\"Predicted state distribution: {np.bincount(predicted_states)}\")"
]
},
{
"cell_type": "markdown",
"id": "047cb02d",
"metadata": {},
"source": [
"## Evaluate Model Performance\n",
"\n",
"Since HMM states are unlabeled, we need to find the best permutation mapping."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "98ad33ea",
"metadata": {},
"outputs": [],
"source": [
"from itertools import permutations\n",
"\n",
"def best_permutation_accuracy(true_states, predicted_states, n_states=3):\n",
" \"\"\"\n",
" Find best permutation mapping and compute accuracy.\n",
" \"\"\"\n",
" best_acc = 0\n",
" best_perm = None\n",
" \n",
" for perm in permutations(range(n_states)):\n",
" mapped = np.array([perm[s] for s in predicted_states])\n",
" acc = np.mean(mapped == true_states)\n",
" if acc > best_acc:\n",
" best_acc = acc\n",
" best_perm = perm\n",
" \n",
" return best_acc, best_perm\n",
"\n",
"accuracy, best_mapping = best_permutation_accuracy(true_states, predicted_states)\n",
"\n",
"print(f\"Best accuracy: {accuracy:.2%}\")\n",
"print(f\"Best mapping: {best_mapping}\")\n",
"print(f\"Interpretation: Predicted state {best_mapping[0]} = Bull\")\n",
"print(f\" Predicted state {best_mapping[1]} = Bear\")\n",
"print(f\" Predicted state {best_mapping[2]} = Sideways\")"
]
},
{
"cell_type": "markdown",
"id": "9d64b9e0",
"metadata": {},
"source": [
"## Visualize Results"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b4296f1a",
"metadata": {},
"outputs": [],
"source": [
"# Apply best mapping\n",
"mapped_predictions = np.array([best_mapping[s] for s in predicted_states])\n",
"\n",
"fig, axes = plt.subplots(3, 1, figsize=(14, 10), sharex=True)\n",
"\n",
"# Plot 1: True states\n",
"for state in range(3):\n",
" mask = true_states == state\n",
" axes[0].scatter(np.where(mask)[0], returns[mask],\n",
" c=colors[state], label=regime_names[state], alpha=0.6, s=20)\n",
"axes[0].set_ylabel('Returns', fontsize=12)\n",
"axes[0].set_title('True Hidden States', fontsize=14, fontweight='bold')\n",
"axes[0].legend()\n",
"axes[0].grid(alpha=0.3)\n",
"\n",
"# Plot 2: Predicted states\n",
"for state in range(3):\n",
" mask = mapped_predictions == state\n",
" axes[1].scatter(np.where(mask)[0], returns[mask],\n",
" c=colors[state], label=f'Predicted {regime_names[state]}', alpha=0.6, s=20)\n",
"axes[1].set_ylabel('Returns', fontsize=12)\n",
"axes[1].set_title(f'Predicted States (Accuracy: {accuracy:.2%})', fontsize=14, fontweight='bold')\n",
"axes[1].legend()\n",
"axes[1].grid(alpha=0.3)\n",
"\n",
"# Plot 3: Errors\n",
"errors = true_states != mapped_predictions\n",
"axes[2].scatter(np.where(errors)[0], returns[errors], \n",
" c='red', marker='x', s=100, label='Misclassified', alpha=0.7)\n",
"axes[2].scatter(np.where(~errors)[0], returns[~errors],\n",
" c='green', marker='.', s=20, label='Correct', alpha=0.3)\n",
"axes[2].set_xlabel('Time', fontsize=12)\n",
"axes[2].set_ylabel('Returns', fontsize=12)\n",
"axes[2].set_title('Classification Errors', fontsize=14, fontweight='bold')\n",
"axes[2].legend()\n",
"axes[2].grid(alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "864a2571",
"metadata": {},
"source": [
"## Confusion Matrix"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "01055ea1",
"metadata": {},
"outputs": [],
"source": [
"from sklearn.metrics import confusion_matrix\n",
"import seaborn as sns\n",
"\n",
"cm = confusion_matrix(true_states, mapped_predictions)\n",
"\n",
"plt.figure(figsize=(8, 6))\n",
"sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
" xticklabels=regime_names, yticklabels=regime_names)\n",
"plt.xlabel('Predicted State', fontsize=12)\n",
"plt.ylabel('True State', fontsize=12)\n",
"plt.title('Confusion Matrix', fontsize=14, fontweight='bold')\n",
"plt.show()\n",
"\n",
"print(\"\\nPer-State Accuracy:\")\n",
"for i, name in enumerate(regime_names):\n",
" acc = cm[i, i] / cm[i].sum()\n",
" print(f\"{name}: {acc:.2%}\")"
]
},
{
"cell_type": "markdown",
"id": "0fa9b358",
"metadata": {},
"source": [
"## Example 2: Comparing Rust vs Python Performance"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5db60bb9",
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"\n",
"# Generate larger dataset\n",
"large_returns, _, _, _ = generate_regime_data(n_samples=5000)\n",
"\n",
"# Time the fitting process\n",
"hmm_bench = HMM(n_states=3, random_state=42)\n",
"\n",
"start = time.time()\n",
"hmm_bench.fit(large_returns, n_iterations=50)\n",
"rust_time = time.time() - start\n",
"\n",
"print(f\"Rust-accelerated fitting time: {rust_time:.3f} seconds\")\n",
"print(f\"For {len(large_returns)} observations with 50 iterations\")\n",
"print(f\"\\nEstimated pure Python time: ~{rust_time * 50:.1f}s (50-100x slower)\")"
]
},
{
"cell_type": "markdown",
"id": "7c91b303",
"metadata": {},
"source": [
"## Key Takeaways\n",
"\n",
"1. **HMMs model sequential data** with hidden states and observable outputs\n",
"2. **Baum-Welch (EM)** learns parameters from unlabeled data\n",
"3. **Viterbi** finds the most likely state sequence\n",
"4. **OptimizR provides 50-100x speedup** over pure Python for large datasets\n",
"5. **Applications:** Finance, speech, biology, weather, NLP\n",
"\n",
"## Further Reading\n",
"\n",
"- Rabiner, L. R. (1989). \"A tutorial on hidden Markov models and selected applications in speech recognition.\"\n",
"- Murphy, K. P. (2012). \"Machine Learning: A Probabilistic Perspective\" - Chapter 17"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}