import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML, display
from collections import deque
class AC3Animator:
"""
Animateur pour visualiser le deroulement de l'algorithme AC-3.
Capture les etats intermediaires pour animation.
"""
def __init__(self):
self.states = []
self.arc_processed = 0
self.values_pruned = 0
def capture_state(self, domains, queue_size, current_arc=None, pruned=None):
"""Capture l'etat courant pour l'animation."""
domains_copy = {v: list(d) for v, d in domains.items()}
self.states.append({
'domains': domains_copy,
'queue_size': queue_size,
'arc': current_arc,
'pruned': pruned or []
})
def animate(self, variable_names=None, title="Animation AC-3"):
"""Cree une animation du processus AC-3."""
if not self.states:
print("Aucun etat capture pour l'animation.")
return None
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
n_vars = len(self.states[0]['domains'])
if variable_names is None:
variable_names = list(self.states[0]['domains'].keys())
def update(frame):
ax1, ax2 = axes
ax1.clear()
ax2.clear()
state = self.states[frame]
domains = state['domains']
positions = range(n_vars)
colors = plt.cm.Set3(range(n_vars))
for i, var in enumerate(variable_names):
domain = domains.get(var, [])
ax1.bar(i, len(domain), color=colors[i], edgecolor='black')
ax1.text(i, len(domain) + 0.1, f"{len(domain)}", ha='center', fontsize=10)
ax1.set_xticks(positions)
ax1.set_xticklabels(variable_names)
ax1.set_ylabel("Taille du domaine")
ax1.set_xlabel("Variable")
ax1.set_ylim(0, max(len(d) for state in self.states for d in state['domains'].values()) + 1)
arc_info = state['arc']
arc_str = f" - Arc: {arc_info}" if arc_info else ""
ax1.set_title(f"Etape {frame+1}/{len(self.states)}{arc_str}")
queue_sizes = [s['queue_size'] for s in self.states[:frame+1]]
ax2.plot(range(len(queue_sizes)), queue_sizes, 'b-o', linewidth=2)
ax2.fill_between(range(len(queue_sizes)), queue_sizes, alpha=0.3)
ax2.axvline(x=frame, color='r', linestyle='--', alpha=0.5)
ax2.set_xlabel("Iteration")
ax2.set_ylabel("Taille de la file")
ax2.set_title("Evolution de la file d'arcs")
ax2.grid(True, alpha=0.3)
if state['pruned']:
ax2.text(0.5, 0.95, f"Valeurs elaguees: {state['pruned']}",
transform=ax2.transAxes, ha='center', va='top',
fontsize=10, color='red')
return axes
anim = FuncAnimation(fig, update, frames=len(self.states),
interval=500, blit=False, repeat=True)
plt.tight_layout()
return HTML(anim.to_jshtml())
def revise_animated(domains, xi, xj, csp):
"""Fonction REVISE pour AC-3 anime."""
revised = False
to_remove = []
for vi in domains[xi]:
has_support = False
for vj in domains[xj]:
if csp.constraint_func(xi, vi, xj, vj):
has_support = True
break
if not has_support:
to_remove.append(vi)
revised = True
for v in to_remove:
domains[xi].remove(v)
return revised
def ac3_with_animation(csp, animator=None):
"""
AC-3 avec capture d'etats pour animation.
"""
if animator is None:
animator = AC3Animator()
domains = {v: list(csp.domains[v]) for v in csp.variables}
queue = deque()
for var in csp.variables:
for neighbor in csp.neighbors[var]:
queue.append((var, neighbor))
animator.capture_state(domains, len(queue))
while queue:
xi, xj = queue.popleft()
if revise_animated(domains, xi, xj, csp):
pruned = [v for v in csp.domains[xi] if v not in domains[xi]]
animator.capture_state(domains, len(queue), (xi, xj), pruned)
if len(domains[xi]) == 0:
return False, domains, animator
for xk in csp.neighbors[xi]:
if xk != xj:
queue.append((xk, xi))
else:
animator.capture_state(domains, len(queue), (xi, xj))
return True, domains, animator
print("Animateur AC-3 pret.")