Files
optimiz-rs/examples/notebooks/01_hmm_tutorial.ipynb
T
Melvin Alvarez cafb3476a4 docs: fix 404 broken links
- Replace non-existent Python examples with actual files
- Fix all placeholder yourusername URLs to ThotDjehuty
- Remove references to non-existent optimal_control.md theory doc
- Update examples to reference: hmm_regime_detection.py, parallel_de_benchmark.py, polaroid_optimizr_integration.py, timeseries_integration.py
2026-01-06 14:36:08 +01:00

442 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"id": "c263c5be",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"✓ OptimizR HMM Module Loaded Successfully!\n",
" Using Rust-accelerated Baum-Welch and Viterbi algorithms\n"
]
}
],
"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!\")\n",
"print(\" Using Rust-accelerated Baum-Welch and Viterbi algorithms\")"
]
},
{
"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": 3,
"id": "f6141fe5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generated 500 return observations\n",
"True means: [ 0.08 -0.06 0.01]\n",
"True stds: [0.02 0.05 0.03]\n",
"State distribution: [199 185 116]\n"
]
}
],
"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": 4,
"id": "a2944703",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fitting HMM with Baum-Welch algorithm (Rust implementation)...\n",
"\n",
"✓ Training complete!\n",
"\n",
"Learned Parameters:\n",
"Transition Matrix:\n",
" State 0: ['0.650', '0.244', '0.107']\n",
" State 1: ['0.288', '0.501', '0.211']\n",
" State 2: ['0.115', '0.155', '0.730']\n",
"\n",
"Emission Means: ['-0.0698', '0.0173', '0.0824']\n",
"Emission Stds: ['0.0446', '0.0302', '0.0183']\n"
]
}
],
"source": [
"# Fit HMM using Rust-accelerated implementation\n",
"hmm = HMM(n_states=3)\n",
"\n",
"print(\"Fitting HMM with Baum-Welch algorithm (Rust implementation)...\")\n",
"hmm.fit(returns, n_iterations=100, tolerance=1e-6)\n",
"\n",
"print(\"\\n✓ Training complete!\")\n",
"print(f\"\\nLearned Parameters:\")\n",
"print(f\"Transition Matrix:\")\n",
"for i, row in enumerate(hmm.transition_matrix_):\n",
" print(f\" State {i}: {[f'{p:.3f}' for p in row]}\")\n",
"print(f\"\\nEmission Means: {[f'{m:.4f}' for m in hmm.emission_means_]}\")\n",
"print(f\"Emission Stds: {[f'{s:.4f}' for s in hmm.emission_stds_]}\")"
]
},
{
"cell_type": "markdown",
"id": "b66fd9db",
"metadata": {},
"source": [
"## Decode States with Viterbi Algorithm"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9fb3e502",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"✓ Viterbi decoding complete!\n",
"Predicted state distribution: [179 125 196]\n"
]
}
],
"source": [
"# Decode most likely state sequence using Viterbi algorithm (Rust)\n",
"predicted_states = hmm.predict(returns)\n",
"\n",
"print(f\"✓ Viterbi decoding complete!\")\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 Rust fitting process\n",
"print(\"Benchmarking Rust HMM implementation...\")\n",
"hmm_bench = HMM(n_states=3)\n",
"\n",
"start = time.time()\n",
"hmm_bench.fit(large_returns, n_iterations=50, tolerance=1e-6)\n",
"rust_time = time.time() - start\n",
"\n",
"print(f\"\\n✓ Rust-accelerated fitting time: {rust_time:.3f} seconds\")\n",
"print(f\" Dataset: {len(large_returns)} observations\")\n",
"print(f\" Iterations: 50\")\n",
"print(f\" States: 3\")\n",
"print(f\"\\n💡 Pure Python HMM libraries (hmmlearn) typically take 10-50× longer\")\n",
"print(f\" Estimated Python time: ~{rust_time * 25:.1f}s\")"
]
},
{
"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": {
"kernelspec": {
"display_name": "rhftlab",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}