MentorFlow / student_agent_dev /memory_decay.py
Cornelius
Deploy MentorFlow with GPU support
a52f96d
raw
history blame
4.55 kB
"""
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
@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}")