Planners-0-Setup

Navigation : Index | << Précédent | 01-Introduction >>

Installation et Configuration de l’Environnement

Ce notebook configure automatiquement l’environnement pour la serie de notebooks sur la Planification Automatique.

Objectifs d’apprentissage

A la fin de ce notebook, vous saurez : 1. Installer et verifier les bibliotheques principales (unified-planning, OR-Tools) 2. Configurer Fast Downward via Docker pour la planification optimale 3. Comprendre la structure de la serie de notebooks 4. Resoudre un problème de planification simple avec PDDL

Prerequis

  • Python 3.9+ installe
  • Docker installe (pour Fast Downward)
  • Connaissances basiques en Python

Duree estimee : 20 minutes


1. Detection de l’environnement

Commencons par detecter le système d’exploitation et la configuration Python.

# Detection de l'environnement
import sys
import os
import platform
import subprocess
import shutil
from pathlib import Path

# Informations systeme
OS_NAME = platform.system()  # 'Windows', 'Linux', 'Darwin'
OS_VERSION = platform.version()
PYTHON_VERSION = sys.version_info
IS_WINDOWS = OS_NAME == 'Windows'
IS_LINUX = OS_NAME == 'Linux'
IS_MAC = OS_NAME == 'Darwin'

print(f"Systeme d'exploitation: {OS_NAME}")
print(f"Version Python: {PYTHON_VERSION.major}.{PYTHON_VERSION.minor}.{PYTHON_VERSION.micro}")
# Chemin portable (relatif au marqueur du depot) — ne leake pas le chemin absolu machine
_cwd_parts = Path.cwd().parts
if "MyIA.AI.Notebooks" in _cwd_parts:
    _repo_rel = Path(*_cwd_parts[_cwd_parts.index("MyIA.AI.Notebooks"):]).as_posix()
else:
    _repo_rel = os.getcwd()
print(f"Repertoire de travail: {_repo_rel}")
print(f"Executable Python: {sys.executable}")
Systeme d'exploitation: Windows
Version Python: 3.13.13
Repertoire de travail: MyIA.AI.Notebooks/SymbolicAI/Planners/00-Environment
Executable Python: <USER_PATH>\AppData\Local\Programs\Python\Python313\python.exe

Interpretation : Detection de l’environnement

Sortie obtenue : le système est Windows, et la cellule imprime la version Python détectée.

Propriete Valeur Impact sur la planification
OS Windows Docker necessite une conversion de chemins
Python imprimee par la cellule — pre-requis : 3.9+ Version recente et compatible
Repertoire MyIA.AI.Notebooks/SymbolicAI/Planners/00-Environment Sous-chemin du depot (portable)

Le pre-requis (3.9+) est ce qui a une valeur pedagogique : il ne bouge pas d’une machine a l’autre. La version detectee, elle, change a chaque montee de version — la recopier ici la mettrait en contradiction avec la sortie d’a-cote des le prochain bump (#9434).

Points cles : 1. La version detectee satisfait le pre-requis 3.9+ : toutes les bibliotheques de planification de la serie sont compatibles 2. Windows necessite une attention particuliere pour les chemins Docker (C:\ → /c/) 3. Le repertoire de travail est correctement positionne dans la serie Planners

Note technique : La detection automatique de l’OS permet d’adapter les commandes Docker (conversion des chemins Windows vers WSL/Git Bash).


2. Technologies de planification

Cette serie utilise plusieurs technologies complementaires :

Technologie Description Usage dans la serie
unified-planning Bibliotheque Python unifiee pour la planification Interface PDDL, modèles
Fast Downward Planificateur optimal (IPC winner) Resolution, heuristiques
OR-Tools Suite d’optimisation Google CP-SAT, scheduling

2.1 Fast Downward

Fast Downward est un système de planification automatisée open-source developpe en C++. Il implemente différents algorithmes de planification bases sur la recherche heuristique :

  • A* avec heuristiques admissibles (optimal)
  • Greedy Best-First Search (satisficing)
  • LAMA (gagnant IPC multiple fois)

2.2 Google OR-Tools

OR-Tools est une suite open-source de logiciels d’optimisation :

  • Programmation par contraintes (CP-SAT)
  • Optimisation lineaire et en nombres entiers
  • Planification de routes (VRP)
  • Ordonnancement (scheduling)

2.3 unified-planning

unified-planning est une bibliotheque Python qui fournit une interface unifiee pour différents planificateurs :

  • Ecriture de modèles PDDL en Python
  • Connexion a plusieurs solveurs (Fast Downward, ENHSP, etc.)
  • Simulation et validation de plans

3. Installation des dependances Python

Nous installons les bibliotheques Python necessaires pour la planification.

# Installation des dependances Python
def install_package(package_name, import_name=None):
    """Installe un package s'il n'est pas deja installe."""
    if import_name is None:
        import_name = package_name
    try:
        __import__(import_name)
        return True
    except ImportError:
        print(f"Installation de {package_name}...")
        result = subprocess.run(
            [sys.executable, '-m', 'pip', 'install', package_name, '-q'],
            capture_output=True, text=True
        )
        if result.returncode == 0:
            print(f"  {package_name} installe avec succes")
            return True
        else:
            print(f"  Echec installation {package_name}: {result.stderr}")
            return False

# Packages essentiels pour la planification
PACKAGES = [
    ('unified-planning', 'unified_planning'),
    ('up-fast-downward', 'up_fast_downward'),  # Plugin Fast Downward
    ('ortools', 'ortools'),
    ('numpy', 'numpy'),
    ('matplotlib', 'matplotlib'),
    ('networkx', 'networkx'),
    ('graphviz', 'graphviz'),
]

print("Verification et installation des dependances Python...")
print("=" * 50)
for pkg_name, import_name in PACKAGES:
    install_package(pkg_name, import_name)
print("=" * 50)
print("Installation des dependances Python terminee.")
Verification et installation des dependances Python...
==================================================
==================================================
Installation des dependances Python terminee.

Interpretation : Installation des dependances

Sortie obtenue : Tous les packages sont déjà installes sur le système.

Package Statut Usage dans la serie
unified-planning Déjà installe API Python pour PDDL
up-fast-downward Déjà installe Plugin Fast Downward
ortools Déjà installe Optimisation CP-SAT
numpy Déjà installe Calculs numériques
matplotlib Déjà installe Visualisations
networkx Déjà installe Graphes d’etats
graphviz Déjà installe Visualisation PDDL

Points cles : 1. L’installation silencieuse (-q) evite d’inonder la sortie avec les details 2. La verification prealable evite de reinstaller des packages déjà presents 3. Tous les outils necessaires pour la serie sont maintenant disponibles

Note technique : Les packages sont installes dans l’environnement Python global. Pour un environnement isole, utilisez venv ou conda env.

3.1 Verification de unified-planning

# Verification de unified-planning
try:
    import unified_planning as up
    from unified_planning.shortcuts import *
    print(f"unified-planning version: {up.__version__}")
    UP_OK = True
except ImportError as e:
    print(f"ERREUR: unified-planning non installe: {e}")
    print("Solution: pip install unified-planning")
    UP_OK = False
unified-planning version: 1.3.0

Interpretation : Verification unified-planning

Sortie obtenue : La bibliotheque unified-planning est installee en version 1.3.0.

Composant Version Signification
unified-planning 1.3.0 Interface Python pour la planification

Points cles : 1. Unified-planning fournit une API Python unifiee pour plusieurs planificateurs 2. Il permet de définir des problemes PDDL programmatiquement sans ecrire de fichiers 3. Les raccourcis from unified_planning.shortcuts import * simplifient la modelisation

Note technique : La version 1.3.0 est stable et supporte la plupart des planificateurs modernes (Fast Downward, ENHSP, pyperplan).

3.2 Verification d’OR-Tools

# Verification d'OR-Tools
try:
    from ortools.sat.python import cp_model
    print("OR-Tools CP-SAT disponible")
    ORTOOLS_OK = True
except ImportError as e:
    print(f"ERREUR: OR-Tools non installe: {e}")
    print("Solution: pip install ortools")
    ORTOOLS_OK = False
OR-Tools CP-SAT disponible

Interpretation : Verification OR-Tools

Sortie obtenue : OR-Tools CP-SAT est correctement installe.

Composant Statut Usage
OR-Tools CP-SAT OK Solveur de programmation par contraintes

Points cles : 1. OR-Tools sera utilise pour les problemes de planification par contraintes (notebook 7) 2. CP-SAT est un solveur moderne et performant pour les problemes mixtes entiers 3. Il permet de modeliser des problemes de scheduling, VRP, et planification temporelle

Note technique : OR-Tools est developpe par Google et utilise en production dans de nombreux systèmes industriels.


4. Configuration Docker pour Fast Downward

Fast Downward est un planificateur C++ qui necessite une compilation. Pour simplifier l’installation et assurer la compatibilite multi-plateforme, nous utilisons Docker avec l’image officielle.

Avantages de l’approche Docker

Aspect Native Docker
Installation Complexe (CMake, g++) Simple (pull image)
Compatibilite Linux/WSL uniquement Toutes plateformes
Reproductibilite Variable Garantie
Maintenance Manuelle Automatique
# Verification de Docker
def check_docker():
    """Verifie si Docker est disponible et operationnel."""
    try:
        result = subprocess.run(
            ['docker', '--version'],
            capture_output=True, text=True, timeout=10
        )
        if result.returncode == 0:
            return True, result.stdout.strip()
        return False, result.stderr
    except FileNotFoundError:
        return False, "Docker non installe"
    except subprocess.TimeoutExpired:
        return False, "Timeout"
    except Exception as e:
        return False, str(e)

DOCKER_OK, DOCKER_INFO = check_docker()

if DOCKER_OK:
    print(f"Docker disponible: {DOCKER_INFO}")
else:
    print(f"Docker non disponible: {DOCKER_INFO}")
Docker disponible: Docker version 29.5.2, build 79eb04c

Interpretation : Verification Docker

Sortie obtenue : Docker est disponible et operationnel sur cette machine.

Composant Statut Signification
Docker OK Moteur de conteneurisation installe et fonctionnel
Version 29.5.2 Version recente et stable

Points cles : 1. Docker permet d’executer Fast Downward dans un environnement isole 2. L’image Docker aiplanning/fast-downward n’est plus disponible publiquement 3. Le plugin up-fast-downward sera utilise a la place pour la planification

Note technique : Si Docker n’est pas disponible, unified-planning peut fonctionner avec d’autres solveurs (pyperplan, ENHSP) mais Fast Downward necessite soit Docker, soit une compilation native.

4.1 Telechargement de l’image Fast Downward

L’image Docker jsboige/coursia-fast-downward contient Fast Downward pre-compile avec un serveur API HTTP sur le port 8200.

# Telechargement et lancement de l'image Fast Downward
# Image Docker avec serveur API Fast Downward (port 8200)
# Reference: docker-configurations/services/planners-downward/

FAST_DOWNWARD_IMAGE = "jsboige/coursia-fast-downward:latest"
FD_API_PORT = 8200
FD_CONTAINER_NAME = "coursia-fast-downward"

def pull_fast_downward():
    """Telecharge l'image Docker Fast Downward."""
    if not DOCKER_OK:
        print("Docker non disponible - impossible de telecharger l'image")
        return False
    
    print(f"Telechargement de l'image {FAST_DOWNWARD_IMAGE}...")
    print("(Ceci peut prendre quelques minutes lors de la premiere execution)")
    
    result = subprocess.run(
        ['docker', 'pull', FAST_DOWNWARD_IMAGE],
        capture_output=True, text=True, timeout=300
    )
    
    if result.returncode == 0:
        print("Image Fast Downward telechargee avec succes")
        return True
    else:
        print(f"Erreur lors du telechargement: {result.stderr}")
        return False

def start_fd_container():
    """Demarre le conteneur Fast Downward en mode serveur API."""
    if not DOCKER_OK:
        return False
    
    # Verifier si le conteneur tourne deja (chemin rapide : reutiliser)
    result = subprocess.run(
        ['docker', 'ps', '-q', '-f', f'name={FD_CONTAINER_NAME}'],
        capture_output=True, text=True
    )
    if result.stdout.strip():
        print(f"Conteneur {FD_CONTAINER_NAME} deja en cours d'execution")
        return True
    
    # Supprimer tout conteneur existant (arrete ou en erreur) avant relancement
    # Evite "container name already in use" quand le conteneur est Exited
    subprocess.run(['docker', 'rm', '-f', FD_CONTAINER_NAME],
                   capture_output=True, text=True, timeout=10)
    
    # Lancer le conteneur en mode detache
    import time
    import urllib.request
    cmd = [
        'docker', 'run', '-d', '--name', FD_CONTAINER_NAME,
        '-p', f'{FD_API_PORT}:{FD_API_PORT}',
        FAST_DOWNWARD_IMAGE
    ]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
    
    if result.returncode != 0:
        print(f"Erreur lancement conteneur: {result.stderr}")
        return False
    
    # Attendre que le serveur soit pret
    for attempt in range(15):
        time.sleep(1)
        try:
            resp = urllib.request.urlopen(f'http://localhost:{FD_API_PORT}/health', timeout=3)
            if resp.status == 200:
                print(f"Conteneur Fast Downward demarre (port {FD_API_PORT})")
                return True
        except Exception:
            pass
    
    print("Timeout: le conteneur n'a pas repondu dans les 15s")
    return False

# Verifier si l'image existe deja
def check_image_exists(image_name):
    """Verifie si une image Docker existe localement."""
    result = subprocess.run(
        ['docker', 'images', '-q', image_name],
        capture_output=True, text=True
    )
    return bool(result.stdout.strip())

if DOCKER_OK:
    if check_image_exists(FAST_DOWNWARD_IMAGE):
        print(f"Image {FAST_DOWNWARD_IMAGE} deja presente localement")
    else:
        pull_fast_downward()
    
    FD_DOCKER_OK = start_fd_container()
else:
    FD_DOCKER_OK = False
    print("Note: Fast Downward via Docker ne sera pas disponible.")
    print("Vous pourrez utiliser unified-planning avec d'autres solveurs.")
Telechargement de l'image jsboige/coursia-fast-downward:latest...
(Ceci peut prendre quelques minutes lors de la premiere execution)
Erreur lors du telechargement: failed to connect to the docker API at npipe:////./pipe/dockerDesktopLinuxEngine; check if the path is correct and if the daemon is running: open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

Erreur lancement conteneur: failed to connect to the docker API at npipe:////./pipe/dockerDesktopLinuxEngine; check if the path is correct and if the daemon is running: open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

4.2 Fonction utilitaire pour Fast Downward Docker

Cette fonction permet d’executer Fast Downward via l’API HTTP du conteneur Docker.

# Fonction utilitaire pour executer Fast Downward via l'API Docker
import json
import urllib.request

def run_fast_downward_docker(domain_pddl: str, problem_pddl: str, 
                             search_config: str = "astar(lmcut())",
                             timeout: int = 60) -> dict:
    """
    Execute Fast Downward via l'API HTTP du conteneur Docker.
    
    Args:
        domain_pddl: Contenu du fichier domaine PDDL
        problem_pddl: Contenu du fichier probleme PDDL
        search_config: Configuration de recherche (defaut: A* avec LM-cut)
        timeout: Timeout en secondes
    
    Returns:
        dict avec 'success', 'plan', 'error', 'time'
    """
    if not DOCKER_OK or not FD_DOCKER_OK:
        return {"success": False, "error": "Fast Downward Docker non disponible"}
    
    import time
    start_time = time.time()
    
    payload = json.dumps({
        "domain": domain_pddl,
        "problem": problem_pddl,
        "search": search_config,
    }).encode("utf-8")
    
    url = f"http://localhost:{FD_API_PORT}/plan"
    req = urllib.request.Request(
        url, data=payload,
        headers={"Content-Type": "application/json"},
        method="POST"
    )
    
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            elapsed = time.time() - start_time
            data = json.loads(resp.read().decode("utf-8"))
            
            if data.get("returncode") == 0:
                return {
                    "success": True,
                    "plan": data.get("stdout", ""),
                    "error": None,
                    "time": elapsed,
                    "search": data.get("search", search_config),
                }
            else:
                return {
                    "success": False,
                    "plan": None,
                    "error": data.get("stderr", "") or data.get("stdout", ""),
                    "time": elapsed,
                    "returncode": data.get("returncode"),
                }
    except urllib.error.URLError as e:
        elapsed = time.time() - start_time
        return {"success": False, "error": f"Connection erreur: {e}", "time": elapsed}
    except Exception as e:
        elapsed = time.time() - start_time
        return {"success": False, "error": str(e), "time": elapsed}

print("Fonction run_fast_downward_docker() prete (API HTTP)")
Fonction run_fast_downward_docker() prete (API HTTP)

5. Resume de l’environnement

Verifions que tous les composants sont correctement installes.

# Resume de l'environnement
print("=" * 60)
print("RESUME DE L'ENVIRONNEMENT PLANIFICATION")
print("=" * 60)
print(f"Plateforme:      {OS_NAME}")
print(f"Python:          {PYTHON_VERSION.major}.{PYTHON_VERSION.minor}.{PYTHON_VERSION.micro}")
print("-" * 60)
print(f"unified-planning: {'OK' if UP_OK else 'MANQUANT (REQUIS)'}")
print(f"OR-Tools:        {'OK' if ORTOOLS_OK else 'MANQUANT (REQUIS)'}")
print(f"Docker:          {'OK' if DOCKER_OK else 'Non disponible'}")
print(f"Fast Downward:   {'OK (Docker)' if FD_DOCKER_OK else 'Non disponible'}")
print("=" * 60)

if UP_OK and ORTOOLS_OK:
    print("\nEnvironnement pret pour les notebooks de planification.")
    
    if FD_DOCKER_OK:
        print("Fast Downward disponible pour la planification optimale.")
    else:
        print("Note: Fast Downward non disponible via Docker.")
        print("Vous pourrez utiliser d'autres solveurs avec unified-planning.")
else:
    print("\nATTENTION: Installez les dependances manquantes.")
    print("Commande: pip install unified-planning ortools")
============================================================
RESUME DE L'ENVIRONNEMENT PLANIFICATION
============================================================
Plateforme:      Windows
Python:          3.13.7
------------------------------------------------------------
unified-planning: OK
OR-Tools:        OK
Docker:          OK
Fast Downward:   Non disponible
============================================================

Environnement pret pour les notebooks de planification.
Note: Fast Downward non disponible via Docker.
Vous pourrez utiliser d'autres solveurs avec unified-planning.

6. Premier exemple concret : Blocks World

Maintenant que l’environnement est configure, appliquons nos connaissances a un problème classique de planification. Le Blocks World est le “Hello World” de la planification automatique.

Pourquoi Blocks World ?

Ce problème presente tous les concepts fondamentaux de la planification : - Etats discrets : configurations des blocs - Actions avec preconditions : ne peut pas prendre un bloc si la main est pleine - Buts a atteindre : configuration cible définie - Espace de recherche : plusieurs sequences possibles

Nous allons définir ce problème en PDDL, puis le resoudre avec unified-planning.



6. Premier exemple : Blocks World

Le Blocks World est le problème classique de planification. Il consiste a empiler des blocs pour atteindre une configuration cible.

Scénario

  • Etat initial : Les blocs A, B, C sont sur la table
  • Objectif : Empiler A sur B, B sur C
  • Actions : pick-up (prendre un bloc), put-down (poser), stack (empiler), unstack (depiler)

6.1 Definition du domaine PDDL

Le domaine définit les types d’objets, les predicats et les actions disponibles.

# Definition du domaine PDDL pour Blocks World
DOMAIN_PDDL = """
(define (domain blocks)
  (:requirements :strips :typing)
  (:types block)
  
  (:predicates
    (on ?x - block ?y - block)
    (ontable ?x - block)
    (clear ?x - block)
    (handempty)
    (holding ?x - block)
  )
  
  (:action pick-up
    :parameters (?x - block)
    :precondition (and (clear ?x) (ontable ?x) (handempty))
    :effect (and (not (ontable ?x)) (not (clear ?x)) 
                 (not (handempty)) (holding ?x))
  )
  
  (:action put-down
    :parameters (?x - block)
    :precondition (holding ?x)
    :effect (and (not (holding ?x)) (clear ?x) 
                 (handempty) (ontable ?x))
  )
  
  (:action stack
    :parameters (?x - block ?y - block)
    :precondition (and (holding ?x) (clear ?y))
    :effect (and (not (holding ?x)) (not (clear ?y)) 
                 (clear ?x) (handempty) (on ?x ?y))
  )
  
  (:action unstack
    :parameters (?x - block ?y - block)
    :precondition (and (on ?x ?y) (clear ?x) (handempty))
    :effect (and (not (on ?x ?y)) (not (clear ?x)) 
                 (not (handempty)) (holding ?x) (clear ?y))
  )
)
"""

print("Domaine PDDL defini: blocks")
print("Actions disponibles: pick-up, put-down, stack, unstack")
Domaine PDDL defini: blocks
Actions disponibles: pick-up, put-down, stack, unstack

Interpretation : Structure du domaine PDDL

Sortie obtenue : Le domaine est défini avec 4 actions et 5 predicats.

Élément Type Signification
block Type Objets manipulables (blocs)
on(?x ?y) Predicat Le bloc x est sur le bloc y
ontable(?x) Predicat Le bloc x est sur la table
clear(?x) Predicat Rien n’est sur le bloc x
handempty Predicat La main est vide
holding(?x) Predicat Le bloc x est dans la main

Actions disponibles : 1. pick-up(?x) : Prendre un bloc depuis la table (precondition: bloc libre, main vide) 2. put-down(?x) : Poser un bloc sur la table (precondition: bloc tenu) 3. stack(?x ?y) : Empiler x sur y (precondition: x tenu, y libre) 4. unstack(?x ?y) : Depiler x de y (precondition: x sur y, main vide, x libre)

Note technique : La syntaxe PDDL utilise des listes prefixees (opérateur arg1 arg2...). Les predicats definissent les etats atomiques possibles, et les actions specifient comment les changer via des preconditions et des effets.

6.2 Definition du problème PDDL

Le problème définit les objets spécifiques, l’etat initial et le but a atteindre.

# Definition du probleme PDDL
# Etat initial: A, B, C sur la table
# Objectif: A sur B, B sur C (tour de 3 blocs)

PROBLEM_PDDL = """
(define (problem blocks-tower)
  (:domain blocks)
  (:objects a b c - block)
  (:init
    (ontable a) (ontable b) (ontable c)
    (clear a) (clear b) (clear c)
    (handempty)
  )
  (:goal (and (on a b) (on b c)))
)
"""

print("Probleme PDDL defini: blocks-tower")
print("Etat initial: A, B, C sur la table")
print("Objectif: A sur B, B sur C")
Probleme PDDL defini: blocks-tower
Etat initial: A, B, C sur la table
Objectif: A sur B, B sur C

Interpretation : Definition du problème PDDL

Sortie obtenue : Le problème est instancie avec 3 blocs et un but a atteindre.

Élément Valeur Signification
Objets a, b, c Trois blocs manipulables
Etat initial Tous sur table Configuration de depart
But A sur B, B sur C Tour A-B-C a construire

Transformation requise : - Etat initial : 3 blocs separes sur la table - Etat final : Tour de 3 blocs (C a la base, B au milieu, A au sommet)

Complexite : - Nombre d’etats possibles : ~1024 (configurations avec 3 blocs) - Actions disponibles : 4 types, parametrables - Plan optimal : 4 actions

Note technique : Le problème PDDL reference le domaine blocks et instancie les predicats pour les objets spécifiques a, b, c. L’etat initial decrit la configuration de depart, et le but decrit la configuration cible.

6.3 Resolution avec unified-planning

Nous utilisons unified-planning pour modeliser et resoudre le problème de maniere programmatique en Python.

# Resolution avec unified-planning
if UP_OK:
    from unified_planning.shortcuts import *
    
    # Creer le probleme en premier
    problem = Problem('blocks-tower')
    
    # Recuperer l'environnement du probleme
    env = problem.environment
    
    # Definir les types via l'environnement
    Block = env.type_manager.UserType('Block')
    
    # Definir les predicats en utilisant des arguments nommes (kwargs)
    # Cela cree automatiquement les Parameter avec le bon environnement
    on = Fluent('on', BoolType(), x=Block, y=Block, environment=env)
    ontable = Fluent('ontable', BoolType(), x=Block, environment=env)
    clear = Fluent('clear', BoolType(), x=Block, environment=env)
    handempty = Fluent('handempty', BoolType(), environment=env)
    holding = Fluent('holding', BoolType(), x=Block, environment=env)
    
    # Ajouter les objets
    a = Object('a', Block)
    b = Object('b', Block)
    c = Object('c', Block)
    problem.add_objects([a, b, c])
    
    # Definir l'etat initial
    problem.set_initial_value(ontable(a), True)
    problem.set_initial_value(ontable(b), True)
    problem.set_initial_value(ontable(c), True)
    problem.set_initial_value(clear(a), True)
    problem.set_initial_value(clear(b), True)
    problem.set_initial_value(clear(c), True)
    problem.set_initial_value(handempty, True)
    
    # Definir le but
    problem.add_goal(on(a, b))
    problem.add_goal(on(b, c))
    
    # Definir les actions
    # IMPORTANT: InstantaneousAction prend des Types, pas des Variables
    # Utiliser action.parameter('name') pour recuperer la variable
    
    # pick-up: prendre un bloc de la table
    pick_up = InstantaneousAction('pick-up', x=Block)
    x = pick_up.parameter('x')
    pick_up.add_precondition(clear(x))
    pick_up.add_precondition(ontable(x))
    pick_up.add_precondition(handempty)
    pick_up.add_effect(ontable(x), False)
    pick_up.add_effect(clear(x), False)
    pick_up.add_effect(handempty, False)
    pick_up.add_effect(holding(x), True)
    problem.add_action(pick_up)
    
    # put-down: poser un bloc sur la table
    put_down = InstantaneousAction('put-down', x=Block)
    x = put_down.parameter('x')
    put_down.add_precondition(holding(x))
    put_down.add_effect(holding(x), False)
    put_down.add_effect(clear(x), True)
    put_down.add_effect(handempty, True)
    put_down.add_effect(ontable(x), True)
    problem.add_action(put_down)
    
    # stack: empiler un bloc sur un autre
    stack = InstantaneousAction('stack', x=Block, y=Block)
    x = stack.parameter('x')
    y = stack.parameter('y')
    stack.add_precondition(holding(x))
    stack.add_precondition(clear(y))
    stack.add_effect(holding(x), False)
    stack.add_effect(clear(y), False)
    stack.add_effect(clear(x), True)
    stack.add_effect(handempty, True)
    stack.add_effect(on(x, y), True)
    problem.add_action(stack)
    
    print("Probleme cree avec unified-planning")
    print(f"Objets: {[o.name for o in problem.all_objects]}")
    print(f"Actions: {[a.name for a in problem.actions]}")
    print(f"Buts: {[str(g) for g in problem.goals]}")
else:
    print("unified-planning non disponible")
Probleme cree avec unified-planning
Objets: ['a', 'b', 'c']
Actions: ['pick-up', 'put-down', 'stack']
Buts: ['on(a, b)', 'on(b, c)']

Interpretation : Modelisation avec unified-planning

Sortie obtenue : Le problème est correctement modelise en Python avec 3 objets, 3 actions et 2 buts.

Concept Python Concept PDDL Signification
UserType('Block') Type block Type d’objets
Fluent('on', ...) Predicat on Etat atomique
InstantaneousAction Action :action Opération elementaire
add_precondition() :precondition Condition d’exécution
add_effect() :effect Changement d’etat

Structure du problème : - Objets : a, b, c (instances du type Block) - Fluents : on, ontable, clear, handempty, holding (etats booléens) - Actions : pick-up, put-down, stack (unstack n’est pas inclus dans cette implementation) - Buts : on(a, b) ET on(b, c)

Points cles : 1. L’API Python permet de définir le problème sans ecrire de fichiers PDDL 2. Les actions sont définies avec des paramètres typés 3. Les preconditions et effets sont exprimés logiquement

Note technique : L’action unstack n’est pas necessaire pour ce problème spécifique car on part d’une configuration ou tous les blocs sont sur la table. Elle serait utile si on partait d’une configuration ou des blocs sont déjà empilés.

6.4 Resolution du problème

Utilisons un planificateur pour trouver une solution.

# Resolution avec un planificateur reel (pyperplan)
if UP_OK:
    from unified_planning.engines import PlanGenerationResultStatus
    # Desactiver les messages de credits pour plus de clarte
    from unified_planning.shortcuts import OneshotPlanner, get_environment
    get_environment().credits_stream = None

    # Afficher le probleme cree
    print("Probleme de planification Blocks World cree avec succes")
    print(f"  - Objets: {[o.name for o in problem.all_objects]}")
    print(f"  - Actions: {[a.name for a in problem.actions]}")
    print(f"  - Buts: {[str(g) for g in problem.goals]}")

    # Invocation REELLE du planificateur pyperplan
    # (pyperplan est installe et utilise dans toute la serie Planners-1..6)
    print("\n" + "=" * 60)
    print("PLANIFICATION AUTOMATIQUE")
    print("=" * 60)
    print("\nResolution du probleme avec le planificateur pyperplan...")

    try:
        with OneshotPlanner(name='pyperplan') as planner:
            result = planner.solve(problem)

        if result.status in (PlanGenerationResultStatus.SOLVED_SATISFICING,
                             PlanGenerationResultStatus.SOLVED_OPTIMALLY):
            plan_actions = list(result.plan.actions)
            print(f"Statut : {result.status.name}")
            print(f"\nPlan trouve ({len(plan_actions)} actions) :")
            print("=" * 40)
            for i, action in enumerate(plan_actions, start=1):
                print(f"  {i}. {action}")
            print("=" * 40)
            print(f"Longueur du plan : {len(plan_actions)} actions")
        else:
            print(f"Aucune solution trouvee (statut : {result.status.name})")
    except Exception as e:
        print(f"Erreur lors de la resolution : {e}")
        print("Verifiez que pyperplan est installe : pip install pyperplan")
else:
    print("unified-planning non disponible")
Probleme de planification Blocks World cree avec succes
  - Objets: ['a', 'b', 'c']
  - Actions: ['pick-up', 'put-down', 'stack']
  - Buts: ['on(a, b)', 'on(b, c)']

============================================================
PLANIFICATION AUTOMATIQUE
============================================================

Resolution du probleme avec le planificateur pyperplan...
Statut : SOLVED_SATISFICING

Plan trouve (4 actions) :
========================================
  1. pick-up(b)
  2. stack(b, c)
  3. pick-up(a)
  4. stack(a, b)
========================================
Longueur du plan : 4 actions

Interpretation du résultat

Le planificateur trouve une sequence d’actions pour passer de l’etat initial a l’etat but :

Étape Action Effet
1 pick-up(b) Main tient B
2 stack(b, c) Empile B sur C
3 pick-up(a) Main tient A
4 stack(a, b) Empile A sur B

Résultat : Tour A-B-C avec C a la base et A au sommet.

Note : Les planificateurs PDDL optimisent généralement la longueur du plan (nombre d’actions). Le plan ci-dessus est optimal (4 actions). L’ordre des actions peut varier selon l’heuristique utilisee, mais la longueur minimale reste la même.


7. Structure de la serie

Cette serie comprend 15+ notebooks organises en 5 parties progressives :

Partie 0 : Environnement (00-Environment/)

# Notebook Contenu Duree
0 Setup Installation, Docker, premier exemple 20 min

Partie 1 : Fondations (01-Foundation/)

# Notebook Contenu Duree
1 Introduction Histoire, concepts, PDDL 30 min
2 PDDL-Basics Syntaxe PDDL : domaines, problèmes, typage 45 min
3 State-Space Espaces d’etats, graphes 30 min

Partie 2 : Planification Classique (02-Classical/)

# Notebook Contenu Duree
4 Fast-Downward A*, GBFS, LAMA 45 min
5 Heuristics h-add, h-max, h-FF, LM-cut 45 min
6 Domains Domaines IPC (Blocks, Logistics, Gripper) 45 min

Partie 3 : Planification Avancee (03-Advanced/)

# Notebook Contenu Duree
7 OR-Tools CP-SAT, contraintes, scheduling 45 min
8 Temporal Durations, parallelisme 45 min
9 HTN Planification hiérarchique, SHOP2 45 min

Partie 4 : Neuro-Symbolique (04-NeuroSymbolic/)

# Notebook Contenu Duree
10 LLM-Planning LLMs, prompting, plan repair 45 min
11 Unified-Planning Interface multi-solveurs 45 min
12 LOOP Learning to Plan 45 min

8. Points cles a retenir

Concepts fondamentaux

Concept Definition
Etat Configuration du monde a un instant donne
Action Transition entre etats (preconditions + effets)
Plan Sequence d’actions menant de l’etat initial au but
Heuristique Estimation du cout pour atteindre le but
PDDL Langage standard pour decrire problemes de planification

Technologies

Technologie Usage
unified-planning Modelisation Python, interface unifiee
Fast Downward Planification optimale (A*, heuristiques)
OR-Tools Optimisation CP-SAT, scheduling
Docker Conteneurisation pour Fast Downward

Prochaines étapes

L’environnement est maintenant configure. Voici la suite de la serie :

  1. Planners-1-Introduction : Histoire de la planification, concepts fondamentaux
  2. Planners-2-PDDL-Basics : Syntaxe PDDL detaillee avec exemples
  3. Planners-3-State-Space : Espaces d’etats et recherche

Notebook suivant : Planners-1-Introduction


9. Conclusion : Environnement pret

Ce notebook a configure l’environnement complet pour la serie de planification automatique.

Resume des acquis

Competence Statut Notebook correspondant
Installation des bibliotheques OK Ce notebook (0)
Comprehension de PDDL Intro Notebook 1-2
Modelisation Python Intro Notebook 2
Resolution de problemes A venir Notebook 4-6

Technologies maitrisees

Technologie Version Usage principal
unified-planning 1.3.0 Modelisation PDDL en Python
OR-Tools Latest Planification par contraintes
Docker 29.5.2 Conteneurisation Fast Downward
Python 3.13 Langage principal

Prochaines étapes

  1. Planners-1-Introduction : Decouvrir l’histoire et les concepts de la planification
  2. Planners-2-PDDL-Basics : Apprendre la syntaxe PDDL en detail
  3. Planners-3-State-Space : Comprendre les espaces d’etats et la recherche

Point cle : La planification automatique est un domaine de l’IA qui consiste a trouver des sequences d’actions pour atteindre des buts. C’est la base de nombreux systèmes : robots, logistique, jeux video, etc.



Exercice : Verification de l’environnement de planification

Verifiez que votre environnement est correctement configure avant de passer aux notebooks suivants.

Étape 1 : Verifier les imports Importez les bibliotheques principales et affichez leurs versions.

Étape 2 : Créer un problème minimal Créez un problème de planification minimal avec unified_planning : 1 fluent, 1 action, 1 objectif.

Étape 3 : Resoudre le problème Utilisez OneshotPlanner avec pyperplan et affichez le plan obtenu.

# TODO etudiant : verification de l'environnement

# Etape 1 : verifier les imports
import unified_planning as up
print(f"unified-planning version : {up.__version__}")

# TODO etudiant : verifier aussi networkx et matplotlib
# import networkx
# import matplotlib

# Etape 2 : creer un probleme minimal (decommenter et completer)
# from unified_planning.shortcuts import *
# problem = Problem("mon_premier_probleme")
# TODO etudiant : ajouter un fluent, une action, un etat initial et un objectif

# Etape 3 : resoudre (decommenter quand le probleme est defini)
# with OneshotPlanner(name="pyperplan") as planner:
#     result = planner.solve(problem)
#     print(f"Statut : {result.status}")
#     print(f"Plan   : {result.plan}")

pass  # Remplacer par votre implementation
unified-planning version : 1.3.0
Retour au sommet