Spaces:
Paused
Paused
| """ | |
| Memory decay model using Ebbinghaus forgetting curve. | |
| Scientific basis: Retention after time t: R(t) = exp(-t / τ) | |
| where τ (tau) is the retention constant. | |
| """ | |
| import numpy as np | |
| from typing import Dict, List | |
| from dataclasses import dataclass | |
| class MemoryRecord: | |
| """Record of practice session for a topic.""" | |
| timestamp: float | |
| base_skill: float # Skill level right after practice | |
| class MemoryDecayModel: | |
| """ | |
| Models realistic forgetting using Ebbinghaus curve. | |
| Key features: | |
| - Track last practice time per topic | |
| - Compute retention factor based on time elapsed | |
| - Effective skill = base_skill × retention_factor | |
| """ | |
| def __init__(self, retention_constant: float = 80.0): | |
| """ | |
| Args: | |
| retention_constant (tau): Controls forgetting speed. | |
| Higher = slower forgetting | |
| tau=80 means ~37% retention after 80 time steps | |
| """ | |
| self.tau = retention_constant | |
| # Track per-topic memory | |
| self.topic_memories: Dict[str, MemoryRecord] = {} | |
| # Current time | |
| self.current_time: float = 0.0 | |
| def update_practice(self, topic: str, base_skill: float): | |
| """ | |
| Record that student just practiced a topic. | |
| Args: | |
| topic: Topic that was practiced | |
| base_skill: Student's skill level after practice (0.0-1.0) | |
| """ | |
| self.topic_memories[topic] = MemoryRecord( | |
| timestamp=self.current_time, | |
| base_skill=base_skill | |
| ) | |
| def get_retention_factor(self, topic: str) -> float: | |
| """ | |
| Compute retention factor for a topic. | |
| Returns: | |
| Retention factor (0.0-1.0) based on Ebbinghaus curve | |
| 1.0 = just practiced, decays exponentially over time | |
| """ | |
| if topic not in self.topic_memories: | |
| return 1.0 # First time seeing topic | |
| memory = self.topic_memories[topic] | |
| time_elapsed = self.current_time - memory.timestamp | |
| # Ebbinghaus forgetting curve | |
| retention = np.exp(-time_elapsed / self.tau) | |
| return retention | |
| def get_effective_skill(self, topic: str) -> float: | |
| """ | |
| Get current effective skill accounting for forgetting. | |
| Returns: | |
| Effective skill = base_skill × retention_factor | |
| """ | |
| if topic not in self.topic_memories: | |
| return 0.0 # Never practiced | |
| memory = self.topic_memories[topic] | |
| retention = self.get_retention_factor(topic) | |
| return memory.base_skill * retention | |
| def get_time_since_practice(self, topic: str) -> float: | |
| """Get time elapsed since last practice.""" | |
| if topic not in self.topic_memories: | |
| return float('inf') | |
| return self.current_time - self.topic_memories[topic].timestamp | |
| def advance_time(self, delta: float = 1.0): | |
| """Simulate time passing.""" | |
| self.current_time += delta | |
| def get_all_topics(self) -> List[str]: | |
| """Get all topics that have been practiced.""" | |
| return list(self.topic_memories.keys()) | |
| def plot_forgetting_curves(self, topics: List[str] = None, | |
| save_path: str = 'forgetting_curves.png'): | |
| """ | |
| Plot forgetting curves for topics. | |
| Shows how retention decays over time since last practice. | |
| """ | |
| import matplotlib.pyplot as plt | |
| if topics is None: | |
| topics = self.get_all_topics() | |
| if not topics: | |
| print("⚠️ No topics to plot") | |
| return | |
| # Generate time points | |
| time_range = np.linspace(0, 200, 100) | |
| plt.figure(figsize=(10, 6)) | |
| for topic in topics: | |
| retentions = [np.exp(-t / self.tau) for t in time_range] | |
| plt.plot(time_range, retentions, label=topic, linewidth=2) | |
| plt.axhline(y=0.5, color='r', linestyle='--', alpha=0.5, | |
| label='50% retention threshold') | |
| plt.xlabel('Time Since Practice', fontsize=12) | |
| plt.ylabel('Retention Factor', fontsize=12) | |
| plt.title('Ebbinghaus Forgetting Curves', fontsize=14) | |
| plt.legend() | |
| plt.grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print(f"📊 Saved forgetting curves to {save_path}") | |