Module mvc.models.graphe

Gestion des graphes

Expand source code
#!/usr/bin/python3
# -*- coding: utf8 -*-

# @author : Sébastien LOZANO
"""Gestion des graphes"""

# Pour les collections
import collections

# Pour les produits cartésiens
from itertools import product

# Pour les copies
import copy  # noqa : F401

# Pour l'aléatoire
import random

# Pour la gestion des imports relatifs
import sys
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(SCRIPT_DIR))

from models.noeud import Noeud  # noqa: E402
from models.arc import Arc  # noqa: E402


class Graphe:
    """La classe Graphe sert pour créer un graphe.

        Attributs
        ---------
        - nom    : str  - Identification du graphe par un nom.

        - noeuds : list - Les noeuds du graphe stockés dans une list.

        - arcs   : list - Les arcs du graphe stockés dans une list.

    """

    def __init__(self, nom: str, noeuds=None, arcs=None) -> None:
        """Constructeur

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> A = Noeud('A')
        >>> B = Noeud('B')
        >>> g1.addNoeud(A)
        >>> g1.addNoeud(B)
        >>> a1 = Arc('A-1->B', A, B, 1)
        >>> a2 = Arc('A-2->B(i)', A, B, 2, True)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
        """
        pass
        assert isinstance(nom, str), "Le nom d'un graphe doit être un string."
        assert isinstance(noeuds, list) or noeuds is None, "Les noeuds d'un graphe sont soit une liste soit None."  # noqa: E501
        assert isinstance(arcs, list) or arcs is None, "Les arcs d'un graphe sont soit une liste soit None."  # noqa: E501

        self.nom = nom
        if noeuds is None:
            self.noeuds = []
        else:
            self.noeuds = noeuds
        if arcs is None:
            self.arcs = []
        else:
            self.arcs = arcs

    def __str__(self) -> None:
        """Représentation en chaine de caractères d'une instance de la classe Graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> A = Noeud('A')
        >>> B = Noeud('B')
        >>> g1.addNoeud(A)
        >>> g1.addNoeud(B)
        >>> a1 = Arc('A-1->B', A, B, 1)
        >>> a2 = Arc('A-2->B(i)', A, B, 2, True)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
        """  # noqa: E501
        pass
        noeuds = []
        for noeud in self.noeuds:
            noeuds.append(noeud.nom)
        arcs = []
        for arc in self.arcs:
            arcs.append(arc.nom)
        return "Graphe(nom={}, noeuds={}, arcs={})".format(
            self.nom,
            noeuds, arcs
        )

# ================================================================
# =========== GETTERS ============================================
# ================================================================
    def getNom(self) -> str:
        """Renvoie le nom du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getNom()
        'graphe1'
        """
        pass
        return self.nom

    def getNoeuds(self) -> list:
        """Renvoie le tableau des noeuds du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addNoeud(n3)
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=n1, voisins=[], nbArcs=0, capital=0)
        Noeud(nom=n2, voisins=[], nbArcs=0, capital=0)
        Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
        """
        pass
        return self.noeuds

    def getArcs(self) -> list:
        """Renvoie le tableau des arcs du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addNoeud(n3)
        >>> a1 = Arc('n1n2_0', n1, n2, 1)
        >>> a2 = Arc('n1n2_1', n1, n2, 2)
        >>> a3 = Arc('n1n3_0', n1, n3, 3)
        >>> a4 = Arc('n2n3_0', n2, n3, 4)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> g1.addArc(a3)
        >>> g1.addArc(a4)
        >>> for arc in g1.getArcs():
        ...     # On n'affiche que les noms des arcs
        ...     print(arc.nom)
        ...
        n1n2_0
        n1n2_1
        n1n3_0
        n2n3_0
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0)
        Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0)
        Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
        """
        pass
        return self.arcs

# ================================================================
# =========== SETTERS ============================================
# ================================================================
    def setNom(self, nouveauNom) -> str:
        """Redéfinit le nom du graphe.

        Paramètres
        ----------
        nouveauNom : str - Le nouveau nom du graphe

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getNom()
        'graphe1'
        >>> g1.setNom('new_graphe1')
        >>> g1.getNom()
        'new_graphe1'
        """
        pass
        assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string."  # noqa : E501    
        self.nom = nouveauNom

    def setNoeuds(self, nouveauxNoeuds: list) -> None:
        """Définit le tableau des noeuds d'une instance de la classe Graphe.

        Paramètres
        ----------
        nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.

        Remarque:
        ---------
        On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> nouveauxNoeuds = [n1, n2, n3]
        >>> g1.setNoeuds(nouveauxNoeuds)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        """
        pass
        # Un booléen pour tester si tout est OK
        allOk = True
        for nouveauNoeud in nouveauxNoeuds:
            assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent."  # noqa : E501
            if nouveauNoeud in self.noeuds:
                allOk = False
                raise ValueError("On ne peut ajouter un noeud déjà présent.")
        if allOk:
            for noeud in nouveauxNoeuds:
                self.addNoeud(noeud)

    def setArcs(self, nouveauxArcs: list) -> None:
        """Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.

        Paramètres
        ----------
        nouveauxArcs : list - Une liste d'instances de la classe Arcs.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> nouveauxNoeuds = [n1, n2, n3]
        >>> g1.setNoeuds(nouveauxNoeuds)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        >>> a1 = Arc('n1n2_0', n1, n2, 1)
        >>> a2 = Arc('n1n2_1', n1, n2, 2)
        >>> a3 = Arc('n1n3_0', n1, n3, 3)
        >>> a4 = Arc('n2n3_0', n2, n3, 4)
        >>> nouveauxArcs = [a1, a2, a3, a4]
        >>> g1.setArcs(nouveauxArcs)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
        """  # noqa : E501
        pass
        # Un booléen pour tester si tout est OK
        allOk = True
        for nouvelArc in nouveauxArcs:
            assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent."  # noqa : E501
            if nouvelArc in self.getArcs():
                allOk = False
                raise ValueError("On ne peut ajouter un arc déjà présent.")
            assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe."  # noqa : E501
            if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or
                    nouvelArc.getNoeudFin() not in self.getNoeuds()):
                allOk = False
                raise ValueError("NouvelArc doit lier deux noeuds du graphe.")
        if allOk:
            for arc in nouveauxArcs:
                self.addArc(arc)

# ================================================================
# =========== OUTILS =============================================
# ================================================================
    def addNoeud(self, noeudCandidat: Noeud) -> None:
        """Ajoute une instance de la classe Noeud au graphe.

        Paramètres
        ----------
        noeudCandidat : Noeud - Un noeud à ajouter au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> g1.addNoeud(n1)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe."  # noqa : E501
        nomsDesNoeudsDuGraphe = []
        for noeud in self.noeuds:
            nomsDesNoeudsDuGraphe.append(noeud.nom)
        assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if self.isNoeudInGraphe(noeudCandidat):
            allOk = False
            raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.')  # noqa : E501
        if noeudCandidat.nom in nomsDesNoeudsDuGraphe:
            allOk = False
            raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
        if allOk:
            self.noeuds.append(noeudCandidat)

    def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.
        
        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2 = Noeud('n1'),Noeud('n2')
        >>> g1.addNoeud(n1)
        >>> g1.isNoeudInGraphe(n1)
        True
        >>> g1.isNoeudInGraphe(n2)
        False
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        isNoeudInGraphe = False
        if noeudCandidat in self.getNoeuds():
            isNoeudInGraphe = True
        return isNoeudInGraphe

    def hasNoeud(self) -> bool:
        """Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe
        est une liste vide ou non.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> g1.hasNoeud()
        False
        >>> g1.addNoeud(n1)
        >>> g1.hasNoeud()
        True
        """  # noqa : E501
        pass

        hasNoeud = False
        if self.getNoeuds():
            hasNoeud = True

        return hasNoeud

    def addArc(self, arcCandidat: Arc) -> None:
        """Ajoute une instance de la classe Arc au graphe.

        Paramètres
        ----------
        arcCandidat : Arc - Un arc à ajouter au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
            >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[])
        >>> arc = Arc('n1n2', n1, n2, 7)
        >>> g1.addArc(arc)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
        """
        pass
        assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
        assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe."  # noqa : E501
        nomsDesArcsDuGraphe = []
        for arc in self.getArcs():
            nomsDesArcsDuGraphe.append(arc.nom)
        assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if arcCandidat in self.getArcs():
            allOk = False
            raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.')  # noqa : E501
        if arcCandidat.nom in nomsDesArcsDuGraphe:
            allOk = False
            raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
        if allOk:
            self.arcs.append(arcCandidat)

    def addArcs(self, arcsToAdd: list) -> None:
        """Ajoute une liste d'instances de la classe Arc au graphe.

        Paramètres
        ----------
        arcsToAdd : list - Une liste d'arcs à ajouter au graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> print(g)
        Graphe(nom=graphe, noeuds=[], arcs=[])
        >>> A,B = Noeud('A'), Noeud('B')
        >>> g.setNoeuds([A,B])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[])
        >>> AB1 = Arc('A->B(1)', A, B, 7)
        >>> g.addArc(AB1)
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)'])
        >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7)
        >>> g.addArcs([AB2,AB3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
        """ # noqa : E501
        pass
        for arc in arcsToAdd:
            assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501

        for arc in arcsToAdd:
            self.addArc(arc)

    def removeArc(self, arcCandidat: Arc) -> None:
        """Supprime une instance de la classe Arc du graphe.

        Paramètres
        ----------
        arcCandidat : Arc - Un arc à supprimer du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        >>> a1 = Arc('n1n2_1', n1, n2, 1)
        >>> a2 = Arc('n1n2_2', n1, n2, 2)
        >>> a3 = Arc('n1n3', n1, n3, 3)
        >>> g1.setArcs([a1,a2,a3])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3'])
        >>> g1.removeArc(a1)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3'])
        >>> for n in g1.getNoeuds():
        ...     print(n)
        ...
        Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0)
        Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
        Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0)
        >>> g1.removeArc(a3)
        >>> for n in g1.getNoeuds():
        ...     print(n)
        ...
        Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0)
        Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
        Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)

        """  # noqa : E501
        pass
        assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
        assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isArcInGraphe(arcCandidat):
            allOk = False
            raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.")  # noqa : E501
        if allOk:
            self.getArcs().remove(arcCandidat)
            # Après suppression de l'arcCandidat,
            # Si il n'y a plus d'arcs entre eux
            # On met à jour le tableau des voisins des Noeuds
            # Sinon, on gère uniquement les nombres d'arcs des Noeuds !
            noeudDebut = arcCandidat.getNoeudDebut()
            noeudFin = arcCandidat.getNoeudFin()
            if ((noeudDebut not in self.getDebiteurs(noeudFin)) and
                    (noeudDebut not in self.getCreanciers(noeudFin))):
                noeudDebut.removeVoisin(noeudFin)
            else:
                noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1)
                noeudFin.setNbArcs(noeudFin.getNbArcs() - 1)

    def isArcInGraphe(self, arcCandidat: Arc) -> bool:
        """Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.

        Paramètres
        ----------
        arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2)
        >>> g1.addArc(a1)
        >>> g1.isArcInGraphe(a1)
        True
        >>> g1.isArcInGraphe(a2)
        False
        """  # noqa : E501
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

        isArcInGraphe = False
        if arcCandidat in self.getArcs():
            isArcInGraphe = True
        return isArcInGraphe

    def hasArc(self) -> bool:
        """Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe
        est une liste vide ou non.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2 = Noeud('n1'),Noeud('n2')
        >>> arc = Arc('n1n2',n1,n2,1)
        >>> g1.hasArc()
        False
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addArc(arc)
        >>> g1.hasArc()
        True
        """  # noqa : E501
        pass

        hasArc = False
        if self.getArcs():
            hasArc = True

        return hasArc

    def getEntreprises(self) -> list:
        """Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises
        de la situation problème étudiée.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getEntreprises()
        []
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> # On affiche uniquement les noms des noeuds pour le doctest
        >>> for noeud in g1.getEntreprises():
        ...     print(noeud.nom)
        ...
        n1
        n2
        n3
        """  # noqa : E501
        pass
        return self.getNoeuds()

    def getDebiteurs(self, noeudCandidat: Noeud) -> list:
        """Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.
        
        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4')
        >>> g1.setNoeuds([n1,n2,n3,n4])
        >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
        >>> g1.setArcs([a1,a2,a3,a4])
        >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest
        >>> for noeud in g1.getDebiteurs(n1):
        ...     print(noeud.nom)
        ...
        n2
        n3
        n4
        >>> for noeud in g1.getDebiteurs(n2):
        ...     print(noeud.nom)
        ...
        
        >>> for noeud in g1.getDebiteurs(n4):
        ...     print(noeud.nom)
        ...
        n1
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            noeudsDebiteurs = []
            for arc in self.getArcs():
                if arc.getNoeudFin() == noeudCandidat:
                    noeudsDebiteurs.append(arc.getNoeudDebut())
        return noeudsDebiteurs

    def getCreanciers(self, noeudCandidat: Noeud) -> list:
        """Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5')
        >>> g1.setNoeuds([n1,n2,n3,n4,n5])
        >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
        >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) 
        >>> g1.setArcs([a1,a2,a3,a4,a5,a6])
        >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest
        >>> for noeud in g1.getCreanciers(n1):
        ...     print(noeud.nom)
        ...
        n4
        n3
        n5
        >>> for noeud in g1.getCreanciers(n2):
        ...     print(noeud.nom)
        ...
        n1
        >>> for noeud in g1.getCreanciers(n5):
        ...     print(noeud.nom)
        ...
        
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            noeudsCreanciers = []
            for arc in self.getArcs():
                if arc.getNoeudDebut() == noeudCandidat:
                    noeudsCreanciers.append(arc.getNoeudFin())
        return noeudsCreanciers

    def positionNette(
        self,
        noeudCandidat: Noeud,
        algo1NomEntreprise=None,
        algo1Don=0
    ) -> int:
        """Retourne la balance entre dettes et créances d'une instance de la classe Noeud.

        Paramètres
        ----------
        - noeudCandidat      : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
        - algo1NomEntreprise : str   - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
        - algo1Don           : int   - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g1.positionNette(A)
        -1
        >>> g1.positionNette(A,'A',5)
        -6
        >>> g1.positionNette(B)
        2
        >>> g1.positionNette(B,'A',5)
        2
        >>> g1.positionNette(C)
        -1
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            # Total des sommes dues aux créanciers du noeudCandidat
            detteTotaleDuNoeud = 0
            # Total des sommes dues par les débiteurs du noeudCandidat
            creanceTotaleDuNoeud = 0
            debiteurs = self.getDebiteurs(noeudCandidat)
            creanciers = self.getCreanciers(noeudCandidat)
            for arc in self.getArcs():
                noeudDebut = arc.getNoeudDebut()
                noeudFin = arc.getNoeudFin()
                if noeudDebut == noeudCandidat and noeudFin in creanciers:
                    detteTotaleDuNoeud += arc.poids
                if noeudDebut in debiteurs and noeudFin == noeudCandidat:
                    creanceTotaleDuNoeud += arc.poids
            if noeudCandidat.getNom() == algo1NomEntreprise:
                positionNetteDuNoeud = (noeudCandidat.getCapital()
                                        + creanceTotaleDuNoeud
                                        - detteTotaleDuNoeud
                                        - algo1Don)
            else:
                positionNetteDuNoeud = (noeudCandidat.getCapital()
                                        + creanceTotaleDuNoeud
                                        - detteTotaleDuNoeud)

        return positionNetteDuNoeud

    def detteGlobale(self) -> int:
        """Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F')
        >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2)
        >>> g1.setNoeuds([E,D,F])
        >>> g1.setArcs([ED,DF,FE])
        >>> g1.detteGlobale()
        19
        >>> g2 = Graphe('graphe2')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g2.setNoeuds([A,B,C])
        >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g2.detteGlobale()
        24
        """  # noqa : E501
        pass
        assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501        

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if allOk:
            detteGlobale = 0
            for arc in self.getArcs():
                detteGlobale += arc.poids

        return detteGlobale

    def simplification(self) -> None:
        """Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B,
        l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB2 = Arc('AB2',A,B,2)
        >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4)
        >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9'])
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A'])
        >>> # Test sur le graphe du document de cours
        >>> g1 = Graphe('graphe1')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC'])
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
        """  # noqa : E501
        pass
        # Si A et B sont voisins alors
        # soit A a des dettes envers B, soit B a des dettes envers A.
        newArcs = []
        for noeud in self.getNoeuds():
            for voisin in noeud.getVoisins():
                detteTotale = 0
                for arc in self.getArcs():
                    if (noeud == arc.getNoeudDebut() and
                            voisin == arc.getNoeudFin()):
                        # On cumule la dette
                        detteTotale += arc.getPoids()
                if detteTotale != 0:
                    # On ajoute un nouvel arc avec la dette totale
                    # au tableau des nouveaux arcs
                    nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom()  # noqa : E501
                    newArc = Arc(nouveauNom, noeud, voisin, detteTotale)
                    newArcs.append(newArc)
        # Le graphe garde les mêmes noeuds
        # Mais on remplace tous les arcs
        # On commence par supprimer tous les arcs existants
        index = len(self.getArcs())-1
        while index != -1:
            self.removeArc(self.getArcs()[index])
            index -= 1
        # On ajoute les nouveaux arcs
        self.setArcs(newArcs)
        # Il faut aussi redefinir tous les voisins
        for arc in newArcs:
            if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()):
                arc.getNoeudDebut().addVoisin(arc.getNoeudFin())
        # Il faut aussi redefinir le nombre d'arcs de chaque noeud
        # On commence par mettre le nombre d'arcs à 0
        for noeud in self.getNoeuds():
            noeud.setNbArcs(0)
            # On met à jour le nombre d'arcs de chaque noeud
            for arc in newArcs:
                if noeud == arc.getNoeudDebut():
                    noeud.setNbArcs(noeud.getNbArcs()+1)
                if noeud == arc.getNoeudFin():
                    noeud.setNbArcs(noeud.getNbArcs()+1)

    def getArcsDirects(self) -> list:
        """Renvoie la liste des arcs directs du graphe.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
        >>> # Pour les tests, on ajoute les arcs inverses à la main
        >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
        >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0)
        >>> arcsInverses = [BA5i,BA2i,CA3i]
        >>> for arc in arcsInverses:
        ...     arc.setEstInverse(True)
        >>> g.addArcs([BA5i,BA2i,CA3i])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
        >>> for arc in g.getArcsDirects():
        ...     print(arc.getNom())
        ...
        A-5->B
        A-2->B
        A-3->C
        >>> g.addArcsInversesNuls()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
        >>> for arc in g.getArcsDirects():
        ...     print(arc.getNom())
        ...
        A-7->B
        A-3->C
        """  # noqa : E501
        pass
        arcsDirects = []
        for arc in self.getArcs():
            if not arc.getEstInverse():
                arcsDirects.append(arc)
        return arcsDirects

    def getArcsInverses(self) -> list:
        """Renvoie la liste des arcs inverses du graphe.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
        >>> # Pour les tests, on ajoute les arcs inverses à la main
        >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
        >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True)
        >>> arcsInverses = [BA5i,BA2i,CA3i]
        >>> g.addArcs([BA5i,BA2i,CA3i])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
        >>> for arc in g.getArcsInverses():
        ...     print(arc.getNom())
        B-5->A(i)
        B-2->A(i)
        C-3->A(i)
        >>> g.addArcsInversesNuls()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
        >>> for arc in g.getArcsInverses():
        ...     print(arc.getNom())
        ...
        B-0->A
        C-0->A
        """  # noqa : E501
        pass
        arcsInverses = []
        for arc in self.getArcs():
            if arc.getEstInverse():
                arcsInverses.append(arc)
        return arcsInverses

    def estSimplifie(self) -> bool:
        """Retourne un booléen selon que le graphe est simplifié ou non.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g1 = copy.deepcopy(g)
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g1.estSimplifie()
        True
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A,B])
        >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1)
        >>> g2.setArcs([AB1,BA1])
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)'])
        >>> g2.addArcsInversesNuls()
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B'])
        >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs
        >>> g2.estSimplifie()
        True
        """  # noqa : E501
        pass
        arcsDirects = self.getArcsDirects()
        estSimplifie = True
        for i in range(len(arcsDirects)):
            k = 0
            for j in range(len(arcsDirects)):
                if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and  # noqa : E501
                        arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()):  # noqa : E501
                    k += 1
            if k > 1:
                estSimplifie = False

        return estSimplifie

    def parcoursEnProfondeurNoeuds(
        self,
        noeudDepart: Noeud,
        dejaVisites=None
    ) -> list:
        """Renvoie les noeuds du graphe accessibles depuis noeudDepart.

        Paramètres
        ----------
        - noeudDepart : Noeud - Le noeud de départ de l'exploration.
        - dejaVisites : list  - Une liste avec les noeuds déjà visités.

        Remarque
        --------
        - Fonction recursive

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for n in g.parcoursEnProfondeurNoeuds(A,[]):
        ...     print(n.getNom())
        ...
        A
        B
        C
        D
        E
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]):
        ...     print(n.getNom())
        ...
        A
        B
        C
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

        if dejaVisites is None:
            dejaVisites = []
        if noeudDepart not in dejaVisites:
            dejaVisites.append(noeudDepart)
            for voisin in self.getCreanciers(noeudDepart):
                self.parcoursEnProfondeurNoeuds(voisin, dejaVisites)

        return dejaVisites

    def getArcsCreanciers(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de tous les arcs partants de noeudDepart.

        Paramètres
        ----------
        noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DE,EA])
        >>> for a in g.getArcsCreanciers(A):
        ...     print(a.getNom())
        ...
        A->B(1)
        A->B(2)
        A->B(3)
        >>> for a in g.getArcsCreanciers(D):
        ...     print(a.getNom())
        ...
        D->E
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudDepart):
            raise ValueError("noeudDepart doit être un noeud du graphe.")
        else:
            arcsCreanciers = []
            for arc in self.getArcs():
                if arc.getNoeudDebut() == noeudDepart:
                    arcsCreanciers.append(arc)
        return arcsCreanciers

    def parcoursEnProfondeurArcs(
        self,
        noeudDepart: Noeud,
        dejaVisites=None
    ) -> list:
        """Renvoie les arcs du graphe accessibles depuis noeudDepart.

        Paramètres
        ----------
        - noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
        - dejaVisites : list  - Les arcs déjà visités.

        Remarque
        --------
        - Fonction recursive

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for n in g.parcoursEnProfondeurArcs(A,[]):
        ...     print(n.getNom())
        ...
        A->B
        C->B
        D->E
        E->A
        A->C
        A->D
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for n in g1.parcoursEnProfondeurArcs(A,[]):
        ...     print(n.getNom())
        ...
        A->B
        B->A
        A->C
        C->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc."  # noqa : E501
        assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

        if dejaVisites is None:
            dejaVisites = []
        for arc in self.getArcsCreanciers(noeudDepart):
            if arc not in dejaVisites:
                dejaVisites.append(arc)
                for voisin in self.getCreanciers(noeudDepart):
                    self.parcoursEnProfondeurArcs(voisin, dejaVisites)

        return dejaVisites

    def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.

        Paramètres
        ----------
        noeudCandidat : Noeud - Un noeud du graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
        >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
        >>> CB = Arc('2->1',C,B,1)
        >>> DE = Arc('3->4',D,E,1)
        >>> EA = Arc('4->0',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
        >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
        >>> print(g.hasCycleFromNoeud(A))
        True
        >>> print(g.hasCycleFromNoeud(B))
        False
        >>> print(g.hasCycleFromNoeud(C))
        False
        >>> print(g.hasCycleFromNoeud(D))
        True
        >>> print(g.hasCycleFromNoeud(E))
        True
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        # Il suffit que l'un des creanciers du noeudCandidat
        # puisse accéder à nouveau à noeudCandidat
        boolDeSortie = False
        index = 1
        noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat)
        for index in range(1, len(noeudsAccessibles)):
            if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]):  # noqa : E501
                boolDeSortie = True
        return boolDeSortie

    def hasCycle(self) -> None:
        """Renvoie un booléen selon que le graphe a encore au moins un cycle.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
        >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
        >>> CB = Arc('2->1',C,B,1)
        >>> DE = Arc('3->4',D,E,1)
        >>> EA = Arc('4->0',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
        >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
        >>> g.hasCycle()
        True
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,AD,CB,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0'])
        >>> # Ce graphe ne contient pas de cycle
        >>> g1.hasCycle()
        False
        """  # noqa : E501
        pass
        boolDeSortie = False
        index = 0
        noeudsDuGraphe = self.getNoeuds()
        while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and
                index < len(noeudsDuGraphe)-1):
            index += 1
        if index < len(noeudsDuGraphe)-1:
            boolDeSortie = True
        return boolDeSortie

    def getNodesWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud,
        way=None
    ) -> list:
        """Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et
        noeudArrivee s'il en existe.
        Renvoie [] sinon.        

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du chemin.
        - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
        - way          : list  - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee

        Remarques
        ---------
        - Fonction recursive
        - Ne trouve pas les cycles 
        - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
        ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])        
        >>> # Aucun chemin de B à C
        >>> for chemin in g.getNodesWays(B,C):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        >>> # Deux chemins entre A et B
        >>> for chemin in g.getNodesWays(A,B):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        ====chemin====
        Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        ====chemin====
        Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        >>> g1 = Graphe('graphe1')
        >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1')
        >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1)
        >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1)
        >>> C1A1 = Arc('C1->A1',C1,A1,1)
        >>> C1D1 = Arc('C1->D1',C1,D1,1)
        >>> g1.setNoeuds([A1,B1,C1,D1])
        >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1])
        >>> # Six chemins entre A1 et D1
        >>> for chemin in g1.getNodesWays(A1,D1):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501

        if way is None:
            way = []
        way = way + [noeudDepart]
        if noeudDepart == noeudArrivee:
            return [way]
        ways = []
        nonVisites = []
        # Pour l'algorithme de Klein, notamment,
        # le fait de rechercher tous les chemins
        # fait exploser le temps de traitement sur des graphes de
        # taille réelle dès Gen_100
        creanciersNoeudDepart = self.getCreanciers(noeudDepart)
        for noeud in creanciersNoeudDepart:
            if noeud not in way:
                nonVisites.append(noeud)

        for noeud in nonVisites:
            ways.extend(self.getNodesWays(noeud, noeudArrivee, way))
        return ways

    def getArcsWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud,
        way=None
    ) -> list:
        """Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee
        s'il en existe.
        Renvoie [] sinon.        

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du chemin.
        - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
        - way          : list  - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.

        Remarques
        ---------        
        - Ne trouve pas les cycles 
        - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
        ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

        Tests
        ----- 
        >>> g = Graphe('grapheLinéaire')
        >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1)
        >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1)
        >>> g.setNoeuds([A,B,C,D])
        >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4])        
        >>> # Il y a 24 chemins d'arcs possibles entre A et D
        >>> len(g.getArcsWays(A,D))
        24
        >>> # Il y a 6 chemins d'arcs possibles entre A et C
        >>> # On les liste tous
        >>> len(g.getArcsWays(A,C))
        6
        >>> for arcsWay in g.getArcsWays(A,C):
        ...     print("=== Chemin d'arcs entre A et C ===")
        ...     for arc in arcsWay:
        ...         print(arc.getNom())
        ...
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(2)
        >>> # Tests sur un graphe non linéaire
        >>> # On ajoute deux arcs entre C et A
        >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1)
        >>> # On ajoute un arc entre A et C
        >>> AC1 = Arc('A->C(1)',A,C,1)
        >>> # Le graphe aura donc un cycle  ABCA
        >>> # La méthode ne tolèrant pas de repasser par le noeud de départ
        >>> # Elle trouve bien 7 chemins entre A et C
        >>> g1 = Graphe('grapheNonLinéaire')
        >>> g1.setNoeuds([A,B,C,D])
        >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1])        
        >>> len(g1.getArcsWays(A,C))
        7
        >>> for arcsWay in g1.getArcsWays(A,C):
        ...     print("=== Chemin d'arcs entre A et C ===")
        ...     for arc in arcsWay:
        ...         print(arc.getNom())
        ...
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->C(1)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts"  # noqa : E501

        # Pour stocker tous les chemins d'arcs possibles
        arcsWays = []
        # On liste tous les chemins de noeuds
        cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee)
        cheminsDeNoeudsSansDoublons = []
        for chemin in cheminsDeNoeudsPossibles:
            if chemin not in cheminsDeNoeudsSansDoublons:
                cheminsDeNoeudsSansDoublons.append(chemin)
        # Pour chaque chemin de cheminsDeNoeudsSansDoublon
        # On va récupérer tous les arcs entre deux noeuds successifs
        # dans arcsWay
        for chemin in cheminsDeNoeudsSansDoublons:
            # le nombre d'espace inter Noeuds
            nbInterNoeuds = len(chemin)-1
            # Pour stocker tous les arcs possibles entre deux noeuds du chemin
            arcsWay = []
            for i in range(nbInterNoeuds):
                arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1]))

            # On va stocker toutes les combinaisons d'arcs de ce chemin
            produitCartesienDesArcs = []
            for combs in product(*arcsWay):
                produitCartesienDesArcs.append(combs)
            arcsWays.extend(produitCartesienDesArcs)

        return arcsWays

    def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie tous les cycles partant du noeudDepart s'il en existe.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ des cycles.

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        D
        E
        A
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for cycle in g1.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        B
        A
        ====cycle====
        A
        C
        B
        A
        >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C')
        >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3)
        >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1)
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A1,B1,C1])
        >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis])
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> # Il y a 6 cycles distincts partant de A1
        >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent.
        >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        C
        A
        ====cycle====
        A
        C
        A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        allCycles = []
        if self.hasCycleFromNoeud(noeudDepart):
            for noeud in self.getCreanciers(noeudDepart):
                for i in range(len(self.getNodesWays(noeud, noeudDepart))):
                    cycle = []
                    cycle = [noeudDepart]
                    cycle.extend(self.getNodesWays(noeud, noeudDepart)[i])
                    allCycles.append(cycle)
        return allCycles

    def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
        """Renvoie tous les arcs entre noeudDepart et noeudArrivee.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DE,EA])
        >>> for a in g.getArcsBetween(A,B):
        ...     print(a.getNom())
        ...
        A->B(1)
        A->B(2)
        A->B(3)
        >>> # Aucun arc de D à A
        >>> for a in g.getArcsBetween(D,A):
        ...     print(a.getNom())
        ...

        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        arcs = []
        for arc in self.getArcs():
            if (arc.getNoeudDebut() == noeudDepart and
                    arc.getNoeudFin() == noeudArrivee):
                arcs.append(arc)
        return arcs

    def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
        """Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

        Remarque
        --------
        Ne trouve pas les cycles
        Choisit un arc au hasard s'il existe plusieurs arcs parallèles.

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> CBbis = Arc('C-2->B',C,B,2)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA])        
        >>> # Aucun chemin de B à C
        >>> for arc in g.getArcsWay(B,C):
        ...     print(arc)
        ...
        >>> # Dans ce graphe trois chemins sont possibles entre A et B
        >>> # Deux des trois chemins présentent des arcs parallèles.
        >>> # On fixe la graine pour les tests
        >>> random.seed(13)        
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->C
        C->B
        >>> random.seed(27)        
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->C
        C-2->B
        >>> random.seed(3)
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        if self.getNodesWays(noeudDepart, noeudArrivee):
            arcsWay = random.choice(
                self.getArcsWays(noeudDepart, noeudArrivee)
            )
        else:
            arcsWay = []

        return arcsWay

    def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe.
        [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du cycle.

        Remarque
        --------
        S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A,B,C])
        >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> # Il y a 6 cycles partants de A dans ce graphe
        >>> # On les récupère tous en fixant la graine
        >>> random.seed(1)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-5->B
        B-5->C
        C-1->A
        >>> random.seed(2)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-5->B
        B-5->C
        C-8->A
        >>> random.seed(6)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-2->B
        B-5->C
        C-1->A
        >>> random.seed(4)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-2->B
        B-5->C
        C-8->A
        >>> random.seed(5)        
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-1->A
        >>> random.seed(24)        
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-8->A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        arcsCycle = []
        if self.hasCycleFromNoeud(noeudDepart):
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if (self.getNodesWays(noeud, noeudDepart) and
                        noeud not in noeudsPossibles):
                    noeudsPossibles.append(noeud)
            # On choisit aléatoirement un premier noeud parmi ceux
            # qui permettent de revenir à noeudDepart
            noeudAleatoire = random.choice(noeudsPossibles)
            # On ajoute le début du chemin
            arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire))
            # On ajoute le reste du chemin
            arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart))

        return arcsCycle

    def getMinPoidsFromCycle(self, cycle: list) -> int:
        """Renvoie le plus petits poids des arcs contenus dans cycle.

        Paramètres
        ----------
        cycle : list - Une liste d'arcs du graphe.

        Remarque
        --------
        Ici on gère les éventuels cycles parallèles

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> random.seed(None)
        >>> random.seed(9)
        >>> cycle = g.getArcsCycleFromNoeud(A)        
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-3->C
        C-1->A
        >>> g.getMinPoidsFromCycle(cycle)
        1
        >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud
        >>> random.seed(15)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-5->B
        B-5->C
        C-8->A
        >>> g.getMinPoidsFromCycle(cycle)
        5
        >>> random.seed(13)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-2->B
        B-5->C
        C-8->A
        >>> g.getMinPoidsFromCycle(cycle)
        2
        >>> random.seed(17)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-2->B
        B-5->C
        C-1->A
        >>> g.getMinPoidsFromCycle(cycle)
        1
        >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud.
        >>> # Mais il peut rester plusieurs cycles partant de A !
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> cycle = g1.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> g1.getMinPoidsFromCycle(cycle)
        5
        """  # noqa : E501
        pass
        # les éléments de cycle doivent être des instances de la classe Arc
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        # Un booléen pour tester si tout est OK
        allOk = True
        for arc in cycle:
            assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc"  # noqa : E501
            if not isinstance(arc, Arc):
                allOk = False
                raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}')  # noqa : E501
            assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément."
            if len(cycle) < 1:
                allOk = False
                raise ValueError('Le cycle doit avoir au moins un élément.')
        if allOk:
            poidsMin = cycle[0].getPoids()
            i = 1
            while i < len(cycle):
                if cycle[i].getPoids() <= poidsMin:
                    poidsMin = cycle[i].getPoids()
                i += 1

        return poidsMin

    def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None:
        """Renvoie l'instance du graphe modifiée.
        Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits
        du plus petit poids des arcs.
        
        Sinon le graphe n'est pas modifié.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ du cycle à éliminer.

        Remarque
        --------
        Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> # Ce graphe contient 2 cycles à partir de A
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        C
        A
        >>> # On élimine le premier, il reste le second
        >>> random.seed(7)
        >>> g.eliminerCycleFromNoeud(A)
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...       
        ====cycle====
        A
        C
        A
        >>> # On élimine le second
        >>> g.eliminerCycleFromNoeud(A)
        >>> # Il n'y a plus de cycle partant de A
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        if self.hasCycleFromNoeud(noeudDepart):
            cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart)
        minPoids = self.getMinPoidsFromCycle(cycleToReduce)
        for arc in cycleToReduce:
            arc.setPoids(arc.getPoids()-minPoids)
            # On renomme l'arc
            nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
            arc.setNom(nouveauNom)
            if arc.getPoids() == 0:
                self.removeArc(arc)

    def hasArcInverse(self, arcCandidat: Arc) -> bool:
        """Renvoie un booléen selon que le graphe contient un arc inverse de
        arcCandidat ou pas.

        Paramètres
        ----------
        arcCandidat  : Arc - L'arc dont on veut savoir si il a un arc inverse.

        Remarques
        ---------
        - L'arc inverse n'est pas forcément un arc inverse dans le sens que
        l'arc d'un cycle de Klein pour être un arc inverse introduit au
        debut de l'algorithme.
        - On suppose que le graphe est simplifié au préalable afin qu'il n'y
        ait qu'un seul arc entre deux noeuds dans un sens donné.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.hasArcInverse(AB)
        False
        >>> # On ajoute tous les arcs inverses au graphe
        >>> g.addArcsInversesNuls()
        >>> g.hasArcInverse(AB)
        True
        """
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

        boolDeSortie = False
        if not arcCandidat.getEstInverse():
            for arc in self.getArcsInverses():
                if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                        arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                    boolDeSortie = True
        if arcCandidat.getEstInverse():
            for arc in self.getArcsDirects():
                if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                        arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                    boolDeSortie = True

        return boolDeSortie

    def getArcInverse(self, arcCandidat: Arc) -> Arc:
        """Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.

        Paramètres
        ----------
        arcCandidat  : Arc - L'arc dont on veut récupérer l'arc inverse.

        Remarques
        ---------
        - L'arc inverse n'est pas forcément un arc inverse dans le sens que
        l'arc d'un cycle de Klein peut être un arc inverse introduit au
        debut de l'algorithme.
        - On suppose que le graphe est simplifié au préalable afin qu'il n'y
        ait qu'un seul arc entre deux noeuds dans un sens donné.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> # On ajoute tous les arcs inverses au graphe
        >>> g.addArcsInversesNuls()
        >>> print(AB.getNom())
        A->B
        >>> print(g.getArcInverse(AB).getNom())
        B-0->A
        """
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501
        assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse."  # noqa : E501

        arcInverse = None
        if self.hasArcInverse(arcCandidat):
            if not arcCandidat.getEstInverse():
                for arc in self.getArcsInverses():
                    if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                            arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                        arcInverse = arc
            if arcCandidat.getEstInverse():
                for arc in self.getArcsDirects():
                    if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                            arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                        arcInverse = arc

        return arcInverse

    def addArcsInversesNuls(self) -> None:
        """Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.

        Remarque
        --------
        Si le graphe n'est pas simplifié, on le simplifie d'abord.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> for arc in g.getArcs():
        ...     print(arc)
        ...
        Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
        Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False)
        Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False)
        Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
        Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
        Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False)
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc)
        ...
        Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False)
        Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False)
        Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False)
        Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False)
        Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
        Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
        Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True)
        Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
        """  # noqa : E501
        pass
        assert self.hasArc(), "Le graphe doit avoir des arcs."
        if not self.estSimplifie():
            self.simplification()
        newArcs = []
        for arc in self.getArcs():
            newName = "{}-0->{}".format(
                arc.getNoeudFin().getNom(),
                arc.getNoeudDebut().getNom()
            )
            newArcInverseNul = Arc(
                newName,
                arc.getNoeudFin(),
                arc.getNoeudDebut(),
                0,
                True
            )
            newArcs.append(newArcInverseNul)
        for arc in newArcs:
            self.addArc(arc)

# ================================================================
# =========== METHODES SPECIFIQUES ALGO KLEIN ====================
# ================================================================

    def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que le graphe présente un cycle de Klein
        ou non à partir du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Remarque
        --------
        Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> AB.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> BA.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> DE.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        False
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        # On récupère tous les noeuds situés à un arc de noeudCancidat
        noeudDirectementAccessibles = self.getCreanciers(noeudCandidat)
        boolDeSortie = False
        for noeud in noeudDirectementAccessibles:
            if self.kleinArcsBetween(noeudCandidat, noeud):
                if self.kleinAllArcsWays(noeud, noeudCandidat):
                    boolDeSortie = True
        return boolDeSortie

    def kleinHasCycle(self) -> None:
        """Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.

        Remarque
        --------
        Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.kleinHasCycle()
        True
        >>> AB.setPoids(0)
        >>> g.kleinHasCycle()
        True
        >>> BA.setPoids(0)
        >>> g.kleinHasCycle()
        True
        >>> DE.setPoids(0)
        >>> g.kleinHasCycle()
        False
        """  # noqa : E501
        pass
        boolDeSortie = False
        index = 0
        noeudsDuGraphe = self.getNoeuds()
        while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and
                index < len(noeudsDuGraphe)-1):
            index += 1
        if index < len(noeudsDuGraphe)-1:
            boolDeSortie = True
        return boolDeSortie

    def kleinArcsBetween(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

        Remarque
        --------
        Méthode utile pour l'algorithme de Klein

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DEg = Arc('D->E',D,E,1)
        >>> EAg = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DEg,EAg])
        >>> for a in g.kleinArcsBetween(A,B):
        ...     print(a.getNom())
        ...        
        A->B(2)
        A->B(3)
        >>> # Aucun arc de D à A
        >>> for a in g.kleinArcsBetween(D,A):
        ...     print(a.getNom())
        ...
        >>> g1 = Graphe('graphe1')
        >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1')
        >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1)
        >>> ABbis = Arc('A1->B1(2)',A1,B1,2)
        >>> CB = Arc('C1->B1',C1,B1,1)
        >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1)
        >>> EA = Arc('E1->A1',E1,A1,1)
        >>> EB = Arc('E1->B1',E1,B1,1)
        >>> g1.setNoeuds([A1,B1,C1,D1,E1])
        >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB])
        >>> # print(g1.kleinArcsBetween(A1,B1))
        >>> for a in g1.kleinArcsBetween(A1,B1):
        ...     print(a.getNom())
        ...
        A1->B1
        A1->B1(2)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        arcs = []
        for arc in self.getArcs():
            if (arc.getNoeudDebut() == noeudDepart and
                    arc.getNoeudFin() == noeudArrivee and
                    arc.getPoids() != 0):
                arcs.append(arc)
        return arcs

    def kleinAllArcsWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie une liste de tuples :

         - d'arcs de poids non nul         
         - et/ou d'arcs inverses de poids non nul         
         - ne passant pas deux fois par le même noeud
        contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs des chemins.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.

        Remarques
        ---------
        - Ne trouve pas les cycles.        
        - Méthode utile pour l'algorithme de Klein.
        - On ne peut pas supprimer de chemins ici en amont de la recherche de cycles

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> ABi = Arc('A->B(i)',A,B,1,True)                
        >>> BA1 = Arc('B->A(1)',B,A,0)
        >>> BAi = Arc('B->A(i)',B,A,2,True)        
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
        >>> g.estSimplifie()
        True
        >>> # Deux chemins de Klein de B à C
        >>> # Mais on ne veut que les chemins :
        >>> # --> qui ont des arcs directs de poids non nul
        >>> # --> et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse qu'un
        >>> for chemin in g.kleinAllArcsWays(B,C):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        B->A(i)
        A->C
        >>> # Dans ce graphe cinq chemins sont possibles entre A et B
        >>> # Mais on ne veut que les chemins :
        >>> # --> qui ont des arcs directs de poids non nul
        >>> # --> et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse que quatre
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins
        >>> AB1.setPoids(1)
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
        >>> # Le chemin doit être supprimé des possibles aussi
        >>> # Cela laisse quatre chemins valides
        >>> EB.setPoids(0)
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
        assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

        # Si il existe au moins un chemin entre noeudDepart et noeudArrivee
        # On ne garde que les chemins :
        #   -> dont les arcs ont des poids non nuls
        #   -> dont les arcs inverses ont des poids non nuls
        #   -> ne passant pas deux fois par le même noeud
        arcsWays = self.getArcsWays(noeudDepart, noeudArrivee)
        if arcsWays:
            # les chemins à garder, a priori vide
            arcsWaysOk = []
            for arcsWay in arcsWays:
                wayToKeep = True
                noeudsConnus = [noeudDepart]
                for arc in arcsWay:
                    if (arc.getPoids() == 0 or
                            arc.getNoeudFin() in noeudsConnus):
                        wayToKeep = False
                    if wayToKeep:
                        noeudsConnus.append(arc.getNoeudFin())
                noeudsConnus = []
                if wayToKeep:
                    arcsWaysOk.append(arcsWay)
        else:
            arcsWaysOk = []

        return arcsWaysOk

    def kleinArcsWay(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie une liste : 
        
        - d'arcs de poids non nul
        - et/ou d'arcs inverses de poids non nul
        - ne passant pas deux fois par le même noeud

        constituant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

        Remarque
        --------
        Ne trouve pas les cycles.        
        Méthode utile pour l'algorithme de Klein.

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> ABi = Arc('A->B(i)',A,B,1,True)                
        >>> BA1 = Arc('B->A(1)',B,A,0)
        >>> BAi = Arc('B->A(i)',B,A,2,True)        
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
        >>> g.estSimplifie()
        True
        >>> # Un seul chemin de Klein de B à C
        >>> for chemin in g.kleinAllArcsWays(B,C):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        B->A(i)
        A->C
        >>> # Dans ce graphe cinq chemins sont possibles entre A et B
        >>> # Mais on ne veut que les chemins avec
        >>> # des arcs directs de poids non nul        
        >>> # et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse que quatre
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On fixe la graine pour en avoir un au hasard
        >>> random.seed(1)             
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())        
        A->C
        C->B
        >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins
        >>> AB1.setPoids(1)        
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On fixe la graine pour en avoir un au hasard
        >>> random.seed(1)
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())
        A->B(i)
        >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
        >>> # Le chemin doit être supprimé des possibles aussi
        >>> # Cela laisse quatre cemins valides        
        >>> EB.setPoids(0)
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On a fixé la graine pour en avoir un au hasard
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())
        A->B(1)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
        assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

        arcsWayOk = []
        kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee)
        if kleinArcsWays:
            arcsWayOk = random.choice(kleinArcsWays)

        return arcsWayOk

    def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe
        
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs des cycles.
        
        Remarques
        ---------
        - Le graphe constitué par les arcs directs doit être simplifié.
        - La méthode kleinAllArcsWays() exclut déjà les chemin passant
        deux fois par le même noeud.
        - On va ici encore exclure certains chemins cycliques à savoir :
            - ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
            - ceux qui n'ont que des arcs inverses
            - ceux qui ont un poids algébrique négatif.
        - Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g.estSimplifie()
        True
        >>> # Il y a 2 cycles partants de A dans ce graphe
        >>> # Ce sont aussi des cycles de Klein                
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-3->C
        C-9->A
        >>> # On ajoute les arcs inverses manuellement pour les tests
        >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
        >>> g.addArcs([BAi,CAi,CBi,ACi])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-7->B
        B-5->C
        C-1->A(i)
        === cycle ===
        A-3->C
        C-9->A
        >>> # Tests sur un autre graphe
        >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G')
        >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4)
        >>> # On ajoute un arc inverse manuellement
        >>> GFi = Arc('G-3->F(i)',G,F,3,True)
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([D,E,F,G])
        >>> g1.setArcs([DE,EF,FG,FD,GFi])
        >>> # Ne présente qu'un cycle DEFD, 
        >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays()
        >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        D-1->E
        E-2->F
        F-4->D
        >>> # Quelques arcs en plus qui pourraient poser problème
        >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6)
        >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True)
        >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi])
        >>> # Il y a 2 cycles valides à partir de D
        >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        D-1->E
        E-2->F
        F-4->D
        === cycle ===
        D-1->E
        E-6->D
        """  # noqa : E501
        pass

        # On récupère tous les chemins cycliques
        allArcsKleinCycles = []
        if self.kleinHasCycleFromNoeud(noeudDepart):
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if (self.kleinAllArcsWays(noeud, noeudDepart) and
                        noeud not in noeudsPossibles):
                    noeudsPossibles.append(noeud)
            # Pour tous les noeuds qui permettent de revenir à noeudDepart
            for noeudPossible in noeudsPossibles:
                arcsKleinCycle = []
                # Pour tous les chemins entre un noeudPossible et noeudDepart
                suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501
                for chemin in suiteChemin:
                    # pour tous les arcs qui permettent de joindre noeudDepart
                    # et noeudPossible
                    debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501
                    for premierArc in debutChemin:
                        # On remet à zéro
                        arcsKleinCycle = []
                        # On ajoute le premier arc
                        arcsKleinCycle.extend([premierArc])
                        # On ajoute le reste du chemin
                        arcsKleinCycle.extend(chemin)
                        # On évite les doublons
                        if arcsKleinCycle not in allArcsKleinCycles:
                            allArcsKleinCycles.append(arcsKleinCycle)

            # Il faut encore nettoyer de certains chemins :
            #   -> ceux qui n'ont que deux arcs qui sont inverses
            #     l'un de l'autre
            #   -> ceux qui n'ont que des arcs inverses
            #   -> ceux qui ont un poids algébrique négatif.
            # ======== Remarque
            # On a besoin d'un variable globale pour éviter l'erreur
            # UnboundLocalError: local variable referenced before assignment python # noqa : E501
            # ========
            global allArcsKleinCyclesCleaned
            allArcsKleinCyclesCleaned = []
            for arcsKleinCycle in allArcsKleinCycles:
                cycleToKeep = True
                # ceux qui n'ont que deux arcs qui sont inverses
                # l'un de l'autre
                if len(arcsKleinCycle) == 2:
                    arc0 = arcsKleinCycle[0]
                    arc1 = arcsKleinCycle[1]
                    if self.hasArcInverse(arc1):
                        if arc0 == self.getArcInverse(arc1):
                            cycleToKeep = False
                    if self.hasArcInverse(arc0):
                        if arc1 == self.getArcInverse(arc0):
                            cycleToKeep = False
                # ceux qui n'ont que des arcs inverses
                onlyArcsInverses = True
                for arc in arcsKleinCycle:
                    if not arc.getEstInverse():
                        onlyArcsInverses = False
                if onlyArcsInverses:
                    cycleToKeep = False
                # ceux qui ont un poids algébrique négatif
                cycleWeight = 0
                for arc in arcsKleinCycle:
                    if arc.getEstInverse():
                        cycleWeight -= arc.getPoids()
                    if not arc.getEstInverse():
                        cycleWeight += arc.getPoids()
                if cycleWeight <= 0:
                    cycleToKeep = False

                if cycleToKeep:
                    allArcsKleinCyclesCleaned.append(arcsKleinCycle)

        return allArcsKleinCyclesCleaned

    def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.

        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du cycle.

        Remarques
        ---------
        - S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
        - La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs
        parallèles entre deux noeuds soient fucionnés.
        - Un cycle ne doit pas passer plusieurs fois par le même noeud


        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g.estSimplifie()
        True
        >>> # Il y a 2 cycles partants de A dans ce graphe
        >>> # Ce sont aussi des cycles de Klein
        >>> # On les récupère tous en fixant la graine
        >>> random.seed(1)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> random.seed(5)        
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-9->A
        >>> # On ajoute les arcs inverses manuellement pour les tests
        >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
        >>> g.addArcs([BAi,CAi,CBi,ACi])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
        >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-7->B
        B-5->C
        C-1->A(i)
        === cycle ===
        A-3->C
        C-9->A
        >>> random.seed(1)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> random.seed(5)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-3->C
        C-9->A
        >>> random.seed(7)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-1->A(i)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

        arcsKleinCycle = []
        if self.kleinHasCycleFromNoeud(noeudDepart):
            allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart)
            arcsKleinCycle = random.choice(allCycles)

        return arcsKleinCycle

    def kleinAllArcsCycles(self) -> list:
        """Renvoie tous les cycles d'un graphe sans doublons.

        Remarques
        ---------
        - Notamment deux cycles sont considérés comme identiques s'ils
        contiennent globalement les mêmes arcs.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
        >>> # Mais 6 cycles distincts
        >>> print(len(G3.kleinAllArcsCycles()))
        6
        >>> for cycle in G3.kleinAllArcsCycles():
        ...     print('=== cycle unique ===')
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle unique ===
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        === cycle unique ===
        A-4->B
        B-2->E
        E-7->F
        F-5->A
        === cycle unique ===
        A-2->E
        E-7->F
        F-5->A
        === cycle unique ===
        B-6->C
        C-5->D
        D-2->B
        === cycle unique ===
        B-2->E
        E-7->F
        F-3->C
        C-5->D
        D-2->B
        === cycle unique ===
        C-5->D
        D-4->E
        E-7->F
        F-3->C
        """
        pass

        # On récupère tous les noeuds
        noeuds = self.getNoeuds()
        # On récupère les cycles au fur et à mesure
        cyclesSansDoublons = []
        # On initialise avec tous les cycles du premier noeud
        for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]):
            cyclesSansDoublons.append(cycle)
        # Pour chaque noeuds
        for noeud in noeuds:
            # Pour chaque cycle partant du noeud
            for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
                toAdd = True
                for cycleUnique in cyclesSansDoublons:
                    if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501
                        toAdd = False
                if toAdd:
                    cyclesSansDoublons.append(cycle)

        return cyclesSansDoublons

    def kleinEstCycle(self, arcsCycle: list) -> bool:
        """Renvoie un booléen selon que arcsCycle est un cycle de Klein du
        graphe ou non.

        Remarque
        --------
        On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut
        les doublons or tous les cycles doivent être testés par exemple
        ABCA et BCAB sont un doublon du même cycle mais doivent être testés
        tous les deux.

        Paramètres
        ----------
        arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
        >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)]
        >>> G3.kleinEstCycle(cycleOK)
        True
        >>> G3.kleinEstCycle(cycleKO)
        False
        """
        pass
        for arc in arcsCycle:
            assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

        # booléen de sortie
        boolDeSortie = True
        # On récupère tous les cycles du graphe
        cyclesDuGraphe = []
        for noeud in self.getNoeuds():
            for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
                cyclesDuGraphe.append(cycle)
        if arcsCycle not in cyclesDuGraphe:
            boolDeSortie = False

        return boolDeSortie

    def kleinModifierCycle(self, arcsCycle: list) -> None:
        """Renvoie le graphe modifié après réduction du cycle.

        Remarque
        --------
        - Chaque arc dans le sens du cycle est modifié en diminuant son poids
        du plus petit poids des arcs du cycle.
        - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
        son poids du plus petit poids des arcs du cycle.

        Paramètres
        ----------
        arcsCycle : list - Le cycle de Klein à réduire.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> G3.addArcsInversesNuls()
        >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
        >>> for arc in cycleOK:
        ...     print(arc.getNom())
        ...
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        >>> G3.kleinModifierCycle(cycleOK)
        >>> for arc in cycleOK:
        ...     print(arc.getNom())
        ...
        A-0->B
        B-2->C
        C-1->D
        D-0->E
        E-3->F
        F-1->A
        """ # noqa : E501
        pass
        assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501
        for arc in arcsCycle:
            assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

        minPoids = self.getMinPoidsFromCycle(arcsCycle)
        for arc in arcsCycle:
            # On diminue le poids de l'arc
            arc.setPoids(arc.getPoids()-minPoids)
            # On augmente le poids de son arc inverse
            arcInverse = self.getArcInverse(arc)
            arcInverse.setPoids(arcInverse.getPoids()+minPoids)
            # On renomme les arcs
            nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
            arc.setNom(nouveauNom)
            nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom()  # noqa : E501
            arcInverse.setNom(nouveauNomInverse)

    def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None:
        """Modifie un cycle de Klein pour un noeud du graphe s'il en existe,
        sinon le graphe n'est pas modifié.

        - Chaque arc dans le sens du cycle est modifié en diminuant son poids
        du plus petit poids des arcs du cycle.
        - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
        son poids du plus petit poids des arcs du cycle.

        Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un
        cycle de Klein.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Remarque
        --------
        - Un cycle de Klein est un cycle :
            - dont tous les arcs ont un poids non nul.        
            - dont le poids algébrique est strictement positif.
        - Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-0->C => arc inverse :  True
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ACA
        >>> random.seed(11)
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-0->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-6->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-3->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-3->C => arc inverse :  True
        >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        === cycle ===
        A-7->B => arc inverse :  False
        B-5->C => arc inverse :  False
        C-6->A => arc inverse :  False
        === cycle ===
        A-7->B => arc inverse :  False
        B-5->C => arc inverse :  False
        C-3->A => arc inverse :  True
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501
        # Pour chaque arc du cycle, il doit y avoir un arc inverse
        for arc in self.getArcs():
            assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse"  # noqa : E501

        if self.kleinHasCycleFromNoeud(noeudCandidat):
            # On choisit un cycle à réduire au hasard
            cycleToChange = random.choice(
                self.kleinAllArcsCyclesFromNoeud(noeudCandidat)
            )
            self.kleinModifierCycle(cycleToChange)

    def kleinDeleteDettesNulles(self) -> None:
        """Supprime tous les arcs de poids nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> random.seed(1)
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ABCA
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-0->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs de poids nul
        >>> g.kleinDeleteDettesNulles()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        """  # noqa : E501
        pass

        # On récupère d'abord tous les arcs à supprimer
        # Car la suppression de proche en proche peut laisser
        # un arc à l'écart à cause de la modification du graphe
        arcsToDelete = []
        for arc in self.getArcs():
            if arc.getPoids() == 0:
                arcsToDelete.append(arc)
        # On supprime alors tous les arcs à supprimer
        for arc in arcsToDelete:
            self.removeArc(arc)

    def kleinDeleteArcsInverses(self) -> None:
        """Supprime tous les arcs inverses temporaires.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-0->C => arc inverse :  True
        >>> random.seed(1)
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ABCA
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-0->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs de poids nul
        >>> g.kleinDeleteDettesNulles()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs inverses temporaires
        >>> g.kleinDeleteArcsInverses()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        """  # noqa : E501
        pass

        # On récupère d'abord tous les arcs à supprimer
        # Car la suppression de proche en proche peut laisser
        # un arc à l'écart à cause de la modification du graphe
        arcsToDelete = []
        for arc in self.getArcs():
            if arc.getEstInverse():
                arcsToDelete.append(arc)
        # On supprime alors tous les arcs à supprimer du graphe
        for arc in arcsToDelete:
            self.removeArc(arc)

# ================================================================
# =========== METHODES NON UTILISÉES =============================
# ================================================================
    def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe.
        [] sinon.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ du cycle.

        Remarque
        --------
        S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for e in g.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        D
        E
        A
        >>> g.getNodesCycleFromNoeud(B)
        []
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> # Il y a deux cycles partants de A dans ce graphe
        >>> random.seed(3)
        >>> for e in g1.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        B
        A
        >>> random.seed(7)
        >>> for e in g1.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        C
        B
        A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        cycle = []
        if self.hasCycleFromNoeud(noeudDepart):
            cycle.append(noeudDepart)
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if self.getNodesWays(noeud, noeudDepart):
                    noeudsPossibles.append(noeud)
            noeudAleatoire = random.choice(noeudsPossibles)
            cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart)
            cheminAleatoire = random.choice(cheminsPossibles)
            cycle.extend(cheminAleatoire)

        return cycle

    def kleinBestArcsCycle(self) -> list:
        """Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.

        Remarques
        ---------
        - Il semble qu'en choisissant à chaque étape le cycle qui réduit
        au maximum la dette globale, la réduction finale ne soit pas la
        meilleure.
        - Cette fonction est donc incorrecte

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
        >>> # Mais 6 cycles distincts
        >>> print(len(G3.kleinAllArcsCycles()))
        6
        >>> G3.addArcsInversesNuls()
        >>> bestCycle1 = G3.kleinBestArcsCycle()
        >>> for arc in bestCycle1:
        ...     print(arc.getNom())
        ...
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        >>> G3.kleinModifierCycle(bestCycle1)
        >>> bestCycle2 = G3.kleinBestArcsCycle()
        >>> for arc in bestCycle2:
        ...     print(arc.getNom())
        ...
        B-2->E
        E-3->F
        F-3->C
        C-4->B
        """
        pass
        # On récupère tous les cycles sans doublons
        allCycles = self.kleinAllArcsCycles()
        bestCycle = []
        reductionDeLaDette = 0
        for cycle in allCycles:
            reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle)
            if reductionDuCycle > reductionDeLaDette:
                reductionDeLaDette = reductionDuCycle
                bestCycle = []
                bestCycle.extend(cycle)

        return bestCycle

    def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple:
        """Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur :
        - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi
        - 2e  : Clef : nom d'un noeud - Valeur : couleur du noeud

        blanc : non visité
        gris  : en cours de visite
        noir  : visité

        Remarques
        ---------
        - J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir.
        Toutefois je ne veux pas perdre le code pour le moment.
        - La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement
        pour qu'elle traite tout le graphe.
        - L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
        - À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris
        tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc
        accessible on dépile.

        Tests
        -----
        >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
        >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H')
        >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1)
        >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1)
        >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1)
        >>> FH = Arc('F->H',F,H,1)
        >>> GE = Arc('G->E',G,E,1)
        >>> HB = Arc('H->B',H,B,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E,F,G,H])
        >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB])
        >>> # Dictionnaire des pères à partir de D
        >>> g.colorierGrapheFromNoeud(D)[0]
        {'D': None, 'E': 'D', 'G': 'D'}
        >>> # Dictionnaire du coloriage partir de D
        >>> g.colorierGrapheFromNoeud(D)[1]
        {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'}
        >>> # Dictionnaire des pères à partir de C
        >>> g.colorierGrapheFromNoeud(C)[0]
        {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'}
        >>> # Dictionnaire du coloriage partir de C
        >>> g.colorierGrapheFromNoeud(C)[1]
        {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

        pile = []
        parents = {}
        couleurs = {}
        for noeud in self.getNoeuds():
            couleurs[noeud.getNom()] = 'blanc'
        parents[noeudDepart.getNom()] = None
        couleurs[noeudDepart.getNom()] = 'gris'
        pile.append(noeudDepart)
        while pile != []:
            pere = pile[-1]
            descendants = []
            for noeud in self.getCreanciers(pere):
                if couleurs[noeud.getNom()] == 'blanc':
                    descendants.append(noeud)
            if descendants != []:
                fils = descendants[0]
                couleurs[fils.getNom()] = 'gris'
                parents[fils.getNom()] = pere.getNom()
                pile.append(fils)
            else:
                pile.pop()
                couleurs[pere.getNom()] = 'noir'
        return parents, couleurs


if __name__ == "__main__":
    import doctest
    doctest.testmod()

Classes

class Graphe (nom: str, noeuds=None, arcs=None)

La classe Graphe sert pour créer un graphe.

Attributs

  • nom : str - Identification du graphe par un nom.

  • noeuds : list - Les noeuds du graphe stockés dans une list.

  • arcs : list - Les arcs du graphe stockés dans une list.

Constructeur

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> A = Noeud('A')
>>> B = Noeud('B')
>>> g1.addNoeud(A)
>>> g1.addNoeud(B)
>>> a1 = Arc('A-1->B', A, B, 1)
>>> a2 = Arc('A-2->B(i)', A, B, 2, True)
>>> g1.addArc(a1)
>>> g1.addArc(a2)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
Expand source code
class Graphe:
    """La classe Graphe sert pour créer un graphe.

        Attributs
        ---------
        - nom    : str  - Identification du graphe par un nom.

        - noeuds : list - Les noeuds du graphe stockés dans une list.

        - arcs   : list - Les arcs du graphe stockés dans une list.

    """

    def __init__(self, nom: str, noeuds=None, arcs=None) -> None:
        """Constructeur

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> A = Noeud('A')
        >>> B = Noeud('B')
        >>> g1.addNoeud(A)
        >>> g1.addNoeud(B)
        >>> a1 = Arc('A-1->B', A, B, 1)
        >>> a2 = Arc('A-2->B(i)', A, B, 2, True)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
        """
        pass
        assert isinstance(nom, str), "Le nom d'un graphe doit être un string."
        assert isinstance(noeuds, list) or noeuds is None, "Les noeuds d'un graphe sont soit une liste soit None."  # noqa: E501
        assert isinstance(arcs, list) or arcs is None, "Les arcs d'un graphe sont soit une liste soit None."  # noqa: E501

        self.nom = nom
        if noeuds is None:
            self.noeuds = []
        else:
            self.noeuds = noeuds
        if arcs is None:
            self.arcs = []
        else:
            self.arcs = arcs

    def __str__(self) -> None:
        """Représentation en chaine de caractères d'une instance de la classe Graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> A = Noeud('A')
        >>> B = Noeud('B')
        >>> g1.addNoeud(A)
        >>> g1.addNoeud(B)
        >>> a1 = Arc('A-1->B', A, B, 1)
        >>> a2 = Arc('A-2->B(i)', A, B, 2, True)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
        """  # noqa: E501
        pass
        noeuds = []
        for noeud in self.noeuds:
            noeuds.append(noeud.nom)
        arcs = []
        for arc in self.arcs:
            arcs.append(arc.nom)
        return "Graphe(nom={}, noeuds={}, arcs={})".format(
            self.nom,
            noeuds, arcs
        )

# ================================================================
# =========== GETTERS ============================================
# ================================================================
    def getNom(self) -> str:
        """Renvoie le nom du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getNom()
        'graphe1'
        """
        pass
        return self.nom

    def getNoeuds(self) -> list:
        """Renvoie le tableau des noeuds du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addNoeud(n3)
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=n1, voisins=[], nbArcs=0, capital=0)
        Noeud(nom=n2, voisins=[], nbArcs=0, capital=0)
        Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
        """
        pass
        return self.noeuds

    def getArcs(self) -> list:
        """Renvoie le tableau des arcs du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addNoeud(n3)
        >>> a1 = Arc('n1n2_0', n1, n2, 1)
        >>> a2 = Arc('n1n2_1', n1, n2, 2)
        >>> a3 = Arc('n1n3_0', n1, n3, 3)
        >>> a4 = Arc('n2n3_0', n2, n3, 4)
        >>> g1.addArc(a1)
        >>> g1.addArc(a2)
        >>> g1.addArc(a3)
        >>> g1.addArc(a4)
        >>> for arc in g1.getArcs():
        ...     # On n'affiche que les noms des arcs
        ...     print(arc.nom)
        ...
        n1n2_0
        n1n2_1
        n1n3_0
        n2n3_0
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0)
        Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0)
        Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
        """
        pass
        return self.arcs

# ================================================================
# =========== SETTERS ============================================
# ================================================================
    def setNom(self, nouveauNom) -> str:
        """Redéfinit le nom du graphe.

        Paramètres
        ----------
        nouveauNom : str - Le nouveau nom du graphe

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getNom()
        'graphe1'
        >>> g1.setNom('new_graphe1')
        >>> g1.getNom()
        'new_graphe1'
        """
        pass
        assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string."  # noqa : E501    
        self.nom = nouveauNom

    def setNoeuds(self, nouveauxNoeuds: list) -> None:
        """Définit le tableau des noeuds d'une instance de la classe Graphe.

        Paramètres
        ----------
        nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.

        Remarque:
        ---------
        On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> nouveauxNoeuds = [n1, n2, n3]
        >>> g1.setNoeuds(nouveauxNoeuds)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        """
        pass
        # Un booléen pour tester si tout est OK
        allOk = True
        for nouveauNoeud in nouveauxNoeuds:
            assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent."  # noqa : E501
            if nouveauNoeud in self.noeuds:
                allOk = False
                raise ValueError("On ne peut ajouter un noeud déjà présent.")
        if allOk:
            for noeud in nouveauxNoeuds:
                self.addNoeud(noeud)

    def setArcs(self, nouveauxArcs: list) -> None:
        """Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.

        Paramètres
        ----------
        nouveauxArcs : list - Une liste d'instances de la classe Arcs.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> n3 = Noeud('n3')
        >>> nouveauxNoeuds = [n1, n2, n3]
        >>> g1.setNoeuds(nouveauxNoeuds)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        >>> a1 = Arc('n1n2_0', n1, n2, 1)
        >>> a2 = Arc('n1n2_1', n1, n2, 2)
        >>> a3 = Arc('n1n3_0', n1, n3, 3)
        >>> a4 = Arc('n2n3_0', n2, n3, 4)
        >>> nouveauxArcs = [a1, a2, a3, a4]
        >>> g1.setArcs(nouveauxArcs)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
        """  # noqa : E501
        pass
        # Un booléen pour tester si tout est OK
        allOk = True
        for nouvelArc in nouveauxArcs:
            assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent."  # noqa : E501
            if nouvelArc in self.getArcs():
                allOk = False
                raise ValueError("On ne peut ajouter un arc déjà présent.")
            assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe."  # noqa : E501
            if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or
                    nouvelArc.getNoeudFin() not in self.getNoeuds()):
                allOk = False
                raise ValueError("NouvelArc doit lier deux noeuds du graphe.")
        if allOk:
            for arc in nouveauxArcs:
                self.addArc(arc)

# ================================================================
# =========== OUTILS =============================================
# ================================================================
    def addNoeud(self, noeudCandidat: Noeud) -> None:
        """Ajoute une instance de la classe Noeud au graphe.

        Paramètres
        ----------
        noeudCandidat : Noeud - Un noeud à ajouter au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
        >>> g1.addNoeud(n1)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe."  # noqa : E501
        nomsDesNoeudsDuGraphe = []
        for noeud in self.noeuds:
            nomsDesNoeudsDuGraphe.append(noeud.nom)
        assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if self.isNoeudInGraphe(noeudCandidat):
            allOk = False
            raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.')  # noqa : E501
        if noeudCandidat.nom in nomsDesNoeudsDuGraphe:
            allOk = False
            raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
        if allOk:
            self.noeuds.append(noeudCandidat)

    def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.
        
        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2 = Noeud('n1'),Noeud('n2')
        >>> g1.addNoeud(n1)
        >>> g1.isNoeudInGraphe(n1)
        True
        >>> g1.isNoeudInGraphe(n2)
        False
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        isNoeudInGraphe = False
        if noeudCandidat in self.getNoeuds():
            isNoeudInGraphe = True
        return isNoeudInGraphe

    def hasNoeud(self) -> bool:
        """Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe
        est une liste vide ou non.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1 = Noeud('n1')
        >>> g1.hasNoeud()
        False
        >>> g1.addNoeud(n1)
        >>> g1.hasNoeud()
        True
        """  # noqa : E501
        pass

        hasNoeud = False
        if self.getNoeuds():
            hasNoeud = True

        return hasNoeud

    def addArc(self, arcCandidat: Arc) -> None:
        """Ajoute une instance de la classe Arc au graphe.

        Paramètres
        ----------
        arcCandidat : Arc - Un arc à ajouter au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
            >>> n1 = Noeud('n1')
        >>> n2 = Noeud('n2')
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[])
        >>> arc = Arc('n1n2', n1, n2, 7)
        >>> g1.addArc(arc)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
        """
        pass
        assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
        assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe."  # noqa : E501
        nomsDesArcsDuGraphe = []
        for arc in self.getArcs():
            nomsDesArcsDuGraphe.append(arc.nom)
        assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if arcCandidat in self.getArcs():
            allOk = False
            raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.')  # noqa : E501
        if arcCandidat.nom in nomsDesArcsDuGraphe:
            allOk = False
            raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
        if allOk:
            self.arcs.append(arcCandidat)

    def addArcs(self, arcsToAdd: list) -> None:
        """Ajoute une liste d'instances de la classe Arc au graphe.

        Paramètres
        ----------
        arcsToAdd : list - Une liste d'arcs à ajouter au graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> print(g)
        Graphe(nom=graphe, noeuds=[], arcs=[])
        >>> A,B = Noeud('A'), Noeud('B')
        >>> g.setNoeuds([A,B])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[])
        >>> AB1 = Arc('A->B(1)', A, B, 7)
        >>> g.addArc(AB1)
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)'])
        >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7)
        >>> g.addArcs([AB2,AB3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
        """ # noqa : E501
        pass
        for arc in arcsToAdd:
            assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501

        for arc in arcsToAdd:
            self.addArc(arc)

    def removeArc(self, arcCandidat: Arc) -> None:
        """Supprime une instance de la classe Arc du graphe.

        Paramètres
        ----------
        arcCandidat : Arc - Un arc à supprimer du graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
        >>> a1 = Arc('n1n2_1', n1, n2, 1)
        >>> a2 = Arc('n1n2_2', n1, n2, 2)
        >>> a3 = Arc('n1n3', n1, n3, 3)
        >>> g1.setArcs([a1,a2,a3])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3'])
        >>> g1.removeArc(a1)
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3'])
        >>> for n in g1.getNoeuds():
        ...     print(n)
        ...
        Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0)
        Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
        Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0)
        >>> g1.removeArc(a3)
        >>> for n in g1.getNoeuds():
        ...     print(n)
        ...
        Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0)
        Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
        Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)

        """  # noqa : E501
        pass
        assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
        assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe."  # noqa : E501

        # Un booléen pour tester si tout est OK
        allOk = True
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isArcInGraphe(arcCandidat):
            allOk = False
            raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.")  # noqa : E501
        if allOk:
            self.getArcs().remove(arcCandidat)
            # Après suppression de l'arcCandidat,
            # Si il n'y a plus d'arcs entre eux
            # On met à jour le tableau des voisins des Noeuds
            # Sinon, on gère uniquement les nombres d'arcs des Noeuds !
            noeudDebut = arcCandidat.getNoeudDebut()
            noeudFin = arcCandidat.getNoeudFin()
            if ((noeudDebut not in self.getDebiteurs(noeudFin)) and
                    (noeudDebut not in self.getCreanciers(noeudFin))):
                noeudDebut.removeVoisin(noeudFin)
            else:
                noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1)
                noeudFin.setNbArcs(noeudFin.getNbArcs() - 1)

    def isArcInGraphe(self, arcCandidat: Arc) -> bool:
        """Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.

        Paramètres
        ----------
        arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2)
        >>> g1.addArc(a1)
        >>> g1.isArcInGraphe(a1)
        True
        >>> g1.isArcInGraphe(a2)
        False
        """  # noqa : E501
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

        isArcInGraphe = False
        if arcCandidat in self.getArcs():
            isArcInGraphe = True
        return isArcInGraphe

    def hasArc(self) -> bool:
        """Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe
        est une liste vide ou non.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2 = Noeud('n1'),Noeud('n2')
        >>> arc = Arc('n1n2',n1,n2,1)
        >>> g1.hasArc()
        False
        >>> g1.addNoeud(n1)
        >>> g1.addNoeud(n2)
        >>> g1.addArc(arc)
        >>> g1.hasArc()
        True
        """  # noqa : E501
        pass

        hasArc = False
        if self.getArcs():
            hasArc = True

        return hasArc

    def getEntreprises(self) -> list:
        """Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises
        de la situation problème étudiée.
        
        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> g1.getEntreprises()
        []
        >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
        >>> g1.setNoeuds([n1,n2,n3])
        >>> # On affiche uniquement les noms des noeuds pour le doctest
        >>> for noeud in g1.getEntreprises():
        ...     print(noeud.nom)
        ...
        n1
        n2
        n3
        """  # noqa : E501
        pass
        return self.getNoeuds()

    def getDebiteurs(self, noeudCandidat: Noeud) -> list:
        """Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.
        
        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4')
        >>> g1.setNoeuds([n1,n2,n3,n4])
        >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
        >>> g1.setArcs([a1,a2,a3,a4])
        >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest
        >>> for noeud in g1.getDebiteurs(n1):
        ...     print(noeud.nom)
        ...
        n2
        n3
        n4
        >>> for noeud in g1.getDebiteurs(n2):
        ...     print(noeud.nom)
        ...
        
        >>> for noeud in g1.getDebiteurs(n4):
        ...     print(noeud.nom)
        ...
        n1
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            noeudsDebiteurs = []
            for arc in self.getArcs():
                if arc.getNoeudFin() == noeudCandidat:
                    noeudsDebiteurs.append(arc.getNoeudDebut())
        return noeudsDebiteurs

    def getCreanciers(self, noeudCandidat: Noeud) -> list:
        """Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5')
        >>> g1.setNoeuds([n1,n2,n3,n4,n5])
        >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
        >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) 
        >>> g1.setArcs([a1,a2,a3,a4,a5,a6])
        >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest
        >>> for noeud in g1.getCreanciers(n1):
        ...     print(noeud.nom)
        ...
        n4
        n3
        n5
        >>> for noeud in g1.getCreanciers(n2):
        ...     print(noeud.nom)
        ...
        n1
        >>> for noeud in g1.getCreanciers(n5):
        ...     print(noeud.nom)
        ...
        
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            noeudsCreanciers = []
            for arc in self.getArcs():
                if arc.getNoeudDebut() == noeudCandidat:
                    noeudsCreanciers.append(arc.getNoeudFin())
        return noeudsCreanciers

    def positionNette(
        self,
        noeudCandidat: Noeud,
        algo1NomEntreprise=None,
        algo1Don=0
    ) -> int:
        """Retourne la balance entre dettes et créances d'une instance de la classe Noeud.

        Paramètres
        ----------
        - noeudCandidat      : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
        - algo1NomEntreprise : str   - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
        - algo1Don           : int   - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g1.positionNette(A)
        -1
        >>> g1.positionNette(A,'A',5)
        -6
        >>> g1.positionNette(B)
        2
        >>> g1.positionNette(B,'A',5)
        2
        >>> g1.positionNette(C)
        -1
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudCandidat):
            raise ValueError("noeudCandidat doit être un noeud du graphe.")
        else:
            # Total des sommes dues aux créanciers du noeudCandidat
            detteTotaleDuNoeud = 0
            # Total des sommes dues par les débiteurs du noeudCandidat
            creanceTotaleDuNoeud = 0
            debiteurs = self.getDebiteurs(noeudCandidat)
            creanciers = self.getCreanciers(noeudCandidat)
            for arc in self.getArcs():
                noeudDebut = arc.getNoeudDebut()
                noeudFin = arc.getNoeudFin()
                if noeudDebut == noeudCandidat and noeudFin in creanciers:
                    detteTotaleDuNoeud += arc.poids
                if noeudDebut in debiteurs and noeudFin == noeudCandidat:
                    creanceTotaleDuNoeud += arc.poids
            if noeudCandidat.getNom() == algo1NomEntreprise:
                positionNetteDuNoeud = (noeudCandidat.getCapital()
                                        + creanceTotaleDuNoeud
                                        - detteTotaleDuNoeud
                                        - algo1Don)
            else:
                positionNetteDuNoeud = (noeudCandidat.getCapital()
                                        + creanceTotaleDuNoeud
                                        - detteTotaleDuNoeud)

        return positionNetteDuNoeud

    def detteGlobale(self) -> int:
        """Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.

        Tests
        -----
        >>> g1 = Graphe('graphe1')
        >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F')
        >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2)
        >>> g1.setNoeuds([E,D,F])
        >>> g1.setArcs([ED,DF,FE])
        >>> g1.detteGlobale()
        19
        >>> g2 = Graphe('graphe2')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g2.setNoeuds([A,B,C])
        >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g2.detteGlobale()
        24
        """  # noqa : E501
        pass
        assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501        

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if allOk:
            detteGlobale = 0
            for arc in self.getArcs():
                detteGlobale += arc.poids

        return detteGlobale

    def simplification(self) -> None:
        """Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B,
        l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB2 = Arc('AB2',A,B,2)
        >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4)
        >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9'])
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A'])
        >>> # Test sur le graphe du document de cours
        >>> g1 = Graphe('graphe1')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC'])
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> for noeud in g1.getNoeuds():
        ...     print(noeud)
        ...
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
        """  # noqa : E501
        pass
        # Si A et B sont voisins alors
        # soit A a des dettes envers B, soit B a des dettes envers A.
        newArcs = []
        for noeud in self.getNoeuds():
            for voisin in noeud.getVoisins():
                detteTotale = 0
                for arc in self.getArcs():
                    if (noeud == arc.getNoeudDebut() and
                            voisin == arc.getNoeudFin()):
                        # On cumule la dette
                        detteTotale += arc.getPoids()
                if detteTotale != 0:
                    # On ajoute un nouvel arc avec la dette totale
                    # au tableau des nouveaux arcs
                    nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom()  # noqa : E501
                    newArc = Arc(nouveauNom, noeud, voisin, detteTotale)
                    newArcs.append(newArc)
        # Le graphe garde les mêmes noeuds
        # Mais on remplace tous les arcs
        # On commence par supprimer tous les arcs existants
        index = len(self.getArcs())-1
        while index != -1:
            self.removeArc(self.getArcs()[index])
            index -= 1
        # On ajoute les nouveaux arcs
        self.setArcs(newArcs)
        # Il faut aussi redefinir tous les voisins
        for arc in newArcs:
            if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()):
                arc.getNoeudDebut().addVoisin(arc.getNoeudFin())
        # Il faut aussi redefinir le nombre d'arcs de chaque noeud
        # On commence par mettre le nombre d'arcs à 0
        for noeud in self.getNoeuds():
            noeud.setNbArcs(0)
            # On met à jour le nombre d'arcs de chaque noeud
            for arc in newArcs:
                if noeud == arc.getNoeudDebut():
                    noeud.setNbArcs(noeud.getNbArcs()+1)
                if noeud == arc.getNoeudFin():
                    noeud.setNbArcs(noeud.getNbArcs()+1)

    def getArcsDirects(self) -> list:
        """Renvoie la liste des arcs directs du graphe.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
        >>> # Pour les tests, on ajoute les arcs inverses à la main
        >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
        >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0)
        >>> arcsInverses = [BA5i,BA2i,CA3i]
        >>> for arc in arcsInverses:
        ...     arc.setEstInverse(True)
        >>> g.addArcs([BA5i,BA2i,CA3i])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
        >>> for arc in g.getArcsDirects():
        ...     print(arc.getNom())
        ...
        A-5->B
        A-2->B
        A-3->C
        >>> g.addArcsInversesNuls()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
        >>> for arc in g.getArcsDirects():
        ...     print(arc.getNom())
        ...
        A-7->B
        A-3->C
        """  # noqa : E501
        pass
        arcsDirects = []
        for arc in self.getArcs():
            if not arc.getEstInverse():
                arcsDirects.append(arc)
        return arcsDirects

    def getArcsInverses(self) -> list:
        """Renvoie la liste des arcs inverses du graphe.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
        >>> # Pour les tests, on ajoute les arcs inverses à la main
        >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
        >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True)
        >>> arcsInverses = [BA5i,BA2i,CA3i]
        >>> g.addArcs([BA5i,BA2i,CA3i])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
        >>> for arc in g.getArcsInverses():
        ...     print(arc.getNom())
        B-5->A(i)
        B-2->A(i)
        C-3->A(i)
        >>> g.addArcsInversesNuls()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
        >>> for arc in g.getArcsInverses():
        ...     print(arc.getNom())
        ...
        B-0->A
        C-0->A
        """  # noqa : E501
        pass
        arcsInverses = []
        for arc in self.getArcs():
            if arc.getEstInverse():
                arcsInverses.append(arc)
        return arcsInverses

    def estSimplifie(self) -> bool:
        """Retourne un booléen selon que le graphe est simplifié ou non.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g1 = copy.deepcopy(g)
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g1.estSimplifie()
        True
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A,B])
        >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1)
        >>> g2.setArcs([AB1,BA1])
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)'])
        >>> g2.addArcsInversesNuls()
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B'])
        >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs
        >>> g2.estSimplifie()
        True
        """  # noqa : E501
        pass
        arcsDirects = self.getArcsDirects()
        estSimplifie = True
        for i in range(len(arcsDirects)):
            k = 0
            for j in range(len(arcsDirects)):
                if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and  # noqa : E501
                        arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()):  # noqa : E501
                    k += 1
            if k > 1:
                estSimplifie = False

        return estSimplifie

    def parcoursEnProfondeurNoeuds(
        self,
        noeudDepart: Noeud,
        dejaVisites=None
    ) -> list:
        """Renvoie les noeuds du graphe accessibles depuis noeudDepart.

        Paramètres
        ----------
        - noeudDepart : Noeud - Le noeud de départ de l'exploration.
        - dejaVisites : list  - Une liste avec les noeuds déjà visités.

        Remarque
        --------
        - Fonction recursive

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for n in g.parcoursEnProfondeurNoeuds(A,[]):
        ...     print(n.getNom())
        ...
        A
        B
        C
        D
        E
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]):
        ...     print(n.getNom())
        ...
        A
        B
        C
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

        if dejaVisites is None:
            dejaVisites = []
        if noeudDepart not in dejaVisites:
            dejaVisites.append(noeudDepart)
            for voisin in self.getCreanciers(noeudDepart):
                self.parcoursEnProfondeurNoeuds(voisin, dejaVisites)

        return dejaVisites

    def getArcsCreanciers(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de tous les arcs partants de noeudDepart.

        Paramètres
        ----------
        noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DE,EA])
        >>> for a in g.getArcsCreanciers(A):
        ...     print(a.getNom())
        ...
        A->B(1)
        A->B(2)
        A->B(3)
        >>> for a in g.getArcsCreanciers(D):
        ...     print(a.getNom())
        ...
        D->E
        """  # noqa : E501
        pass
        assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.isNoeudInGraphe(noeudDepart):
            raise ValueError("noeudDepart doit être un noeud du graphe.")
        else:
            arcsCreanciers = []
            for arc in self.getArcs():
                if arc.getNoeudDebut() == noeudDepart:
                    arcsCreanciers.append(arc)
        return arcsCreanciers

    def parcoursEnProfondeurArcs(
        self,
        noeudDepart: Noeud,
        dejaVisites=None
    ) -> list:
        """Renvoie les arcs du graphe accessibles depuis noeudDepart.

        Paramètres
        ----------
        - noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
        - dejaVisites : list  - Les arcs déjà visités.

        Remarque
        --------
        - Fonction recursive

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for n in g.parcoursEnProfondeurArcs(A,[]):
        ...     print(n.getNom())
        ...
        A->B
        C->B
        D->E
        E->A
        A->C
        A->D
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for n in g1.parcoursEnProfondeurArcs(A,[]):
        ...     print(n.getNom())
        ...
        A->B
        B->A
        A->C
        C->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc."  # noqa : E501
        assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

        if dejaVisites is None:
            dejaVisites = []
        for arc in self.getArcsCreanciers(noeudDepart):
            if arc not in dejaVisites:
                dejaVisites.append(arc)
                for voisin in self.getCreanciers(noeudDepart):
                    self.parcoursEnProfondeurArcs(voisin, dejaVisites)

        return dejaVisites

    def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.

        Paramètres
        ----------
        noeudCandidat : Noeud - Un noeud du graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
        >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
        >>> CB = Arc('2->1',C,B,1)
        >>> DE = Arc('3->4',D,E,1)
        >>> EA = Arc('4->0',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
        >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
        >>> print(g.hasCycleFromNoeud(A))
        True
        >>> print(g.hasCycleFromNoeud(B))
        False
        >>> print(g.hasCycleFromNoeud(C))
        False
        >>> print(g.hasCycleFromNoeud(D))
        True
        >>> print(g.hasCycleFromNoeud(E))
        True
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        # Il suffit que l'un des creanciers du noeudCandidat
        # puisse accéder à nouveau à noeudCandidat
        boolDeSortie = False
        index = 1
        noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat)
        for index in range(1, len(noeudsAccessibles)):
            if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]):  # noqa : E501
                boolDeSortie = True
        return boolDeSortie

    def hasCycle(self) -> None:
        """Renvoie un booléen selon que le graphe a encore au moins un cycle.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
        >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
        >>> CB = Arc('2->1',C,B,1)
        >>> DE = Arc('3->4',D,E,1)
        >>> EA = Arc('4->0',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
        >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
        >>> g.hasCycle()
        True
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,AD,CB,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0'])
        >>> # Ce graphe ne contient pas de cycle
        >>> g1.hasCycle()
        False
        """  # noqa : E501
        pass
        boolDeSortie = False
        index = 0
        noeudsDuGraphe = self.getNoeuds()
        while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and
                index < len(noeudsDuGraphe)-1):
            index += 1
        if index < len(noeudsDuGraphe)-1:
            boolDeSortie = True
        return boolDeSortie

    def getNodesWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud,
        way=None
    ) -> list:
        """Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et
        noeudArrivee s'il en existe.
        Renvoie [] sinon.        

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du chemin.
        - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
        - way          : list  - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee

        Remarques
        ---------
        - Fonction recursive
        - Ne trouve pas les cycles 
        - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
        ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])        
        >>> # Aucun chemin de B à C
        >>> for chemin in g.getNodesWays(B,C):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        >>> # Deux chemins entre A et B
        >>> for chemin in g.getNodesWays(A,B):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        ====chemin====
        Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        ====chemin====
        Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
        >>> g1 = Graphe('graphe1')
        >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1')
        >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1)
        >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1)
        >>> C1A1 = Arc('C1->A1',C1,A1,1)
        >>> C1D1 = Arc('C1->D1',C1,D1,1)
        >>> g1.setNoeuds([A1,B1,C1,D1])
        >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1])
        >>> # Six chemins entre A1 et D1
        >>> for chemin in g1.getNodesWays(A1,D1):
        ...     print("====chemin====")
        ...     for n in chemin:
        ...         print(n)
        ...
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        ====chemin====
        Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
        Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
        Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
        Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501

        if way is None:
            way = []
        way = way + [noeudDepart]
        if noeudDepart == noeudArrivee:
            return [way]
        ways = []
        nonVisites = []
        # Pour l'algorithme de Klein, notamment,
        # le fait de rechercher tous les chemins
        # fait exploser le temps de traitement sur des graphes de
        # taille réelle dès Gen_100
        creanciersNoeudDepart = self.getCreanciers(noeudDepart)
        for noeud in creanciersNoeudDepart:
            if noeud not in way:
                nonVisites.append(noeud)

        for noeud in nonVisites:
            ways.extend(self.getNodesWays(noeud, noeudArrivee, way))
        return ways

    def getArcsWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud,
        way=None
    ) -> list:
        """Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee
        s'il en existe.
        Renvoie [] sinon.        

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du chemin.
        - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
        - way          : list  - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.

        Remarques
        ---------        
        - Ne trouve pas les cycles 
        - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
        ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

        Tests
        ----- 
        >>> g = Graphe('grapheLinéaire')
        >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1)
        >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1)
        >>> g.setNoeuds([A,B,C,D])
        >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4])        
        >>> # Il y a 24 chemins d'arcs possibles entre A et D
        >>> len(g.getArcsWays(A,D))
        24
        >>> # Il y a 6 chemins d'arcs possibles entre A et C
        >>> # On les liste tous
        >>> len(g.getArcsWays(A,C))
        6
        >>> for arcsWay in g.getArcsWays(A,C):
        ...     print("=== Chemin d'arcs entre A et C ===")
        ...     for arc in arcsWay:
        ...         print(arc.getNom())
        ...
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(2)
        >>> # Tests sur un graphe non linéaire
        >>> # On ajoute deux arcs entre C et A
        >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1)
        >>> # On ajoute un arc entre A et C
        >>> AC1 = Arc('A->C(1)',A,C,1)
        >>> # Le graphe aura donc un cycle  ABCA
        >>> # La méthode ne tolèrant pas de repasser par le noeud de départ
        >>> # Elle trouve bien 7 chemins entre A et C
        >>> g1 = Graphe('grapheNonLinéaire')
        >>> g1.setNoeuds([A,B,C,D])
        >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1])        
        >>> len(g1.getArcsWays(A,C))
        7
        >>> for arcsWay in g1.getArcsWays(A,C):
        ...     print("=== Chemin d'arcs entre A et C ===")
        ...     for arc in arcsWay:
        ...         print(arc.getNom())
        ...
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(1)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(2)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(1)
        === Chemin d'arcs entre A et C ===
        A->B(3)
        B->C(2)
        === Chemin d'arcs entre A et C ===
        A->C(1)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts"  # noqa : E501

        # Pour stocker tous les chemins d'arcs possibles
        arcsWays = []
        # On liste tous les chemins de noeuds
        cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee)
        cheminsDeNoeudsSansDoublons = []
        for chemin in cheminsDeNoeudsPossibles:
            if chemin not in cheminsDeNoeudsSansDoublons:
                cheminsDeNoeudsSansDoublons.append(chemin)
        # Pour chaque chemin de cheminsDeNoeudsSansDoublon
        # On va récupérer tous les arcs entre deux noeuds successifs
        # dans arcsWay
        for chemin in cheminsDeNoeudsSansDoublons:
            # le nombre d'espace inter Noeuds
            nbInterNoeuds = len(chemin)-1
            # Pour stocker tous les arcs possibles entre deux noeuds du chemin
            arcsWay = []
            for i in range(nbInterNoeuds):
                arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1]))

            # On va stocker toutes les combinaisons d'arcs de ce chemin
            produitCartesienDesArcs = []
            for combs in product(*arcsWay):
                produitCartesienDesArcs.append(combs)
            arcsWays.extend(produitCartesienDesArcs)

        return arcsWays

    def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie tous les cycles partant du noeudDepart s'il en existe.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ des cycles.

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        D
        E
        A
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> for cycle in g1.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        B
        A
        ====cycle====
        A
        C
        B
        A
        >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C')
        >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3)
        >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1)
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A1,B1,C1])
        >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis])
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> # Il y a 6 cycles distincts partant de A1
        >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent.
        >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1):
        ...     print("====cycle====")
        ...     for n in cycle:
        ...         print(n.getNom())
        ...
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        C
        A
        ====cycle====
        A
        C
        A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        allCycles = []
        if self.hasCycleFromNoeud(noeudDepart):
            for noeud in self.getCreanciers(noeudDepart):
                for i in range(len(self.getNodesWays(noeud, noeudDepart))):
                    cycle = []
                    cycle = [noeudDepart]
                    cycle.extend(self.getNodesWays(noeud, noeudDepart)[i])
                    allCycles.append(cycle)
        return allCycles

    def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
        """Renvoie tous les arcs entre noeudDepart et noeudArrivee.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DE,EA])
        >>> for a in g.getArcsBetween(A,B):
        ...     print(a.getNom())
        ...
        A->B(1)
        A->B(2)
        A->B(3)
        >>> # Aucun arc de D à A
        >>> for a in g.getArcsBetween(D,A):
        ...     print(a.getNom())
        ...

        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        arcs = []
        for arc in self.getArcs():
            if (arc.getNoeudDebut() == noeudDepart and
                    arc.getNoeudFin() == noeudArrivee):
                arcs.append(arc)
        return arcs

    def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
        """Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

        Remarque
        --------
        Ne trouve pas les cycles
        Choisit un arc au hasard s'il existe plusieurs arcs parallèles.

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> CBbis = Arc('C-2->B',C,B,2)
        >>> DE = Arc('D->E',D,E,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA])        
        >>> # Aucun chemin de B à C
        >>> for arc in g.getArcsWay(B,C):
        ...     print(arc)
        ...
        >>> # Dans ce graphe trois chemins sont possibles entre A et B
        >>> # Deux des trois chemins présentent des arcs parallèles.
        >>> # On fixe la graine pour les tests
        >>> random.seed(13)        
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->C
        C->B
        >>> random.seed(27)        
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->C
        C-2->B
        >>> random.seed(3)
        >>> for arc in g.getArcsWay(A,B):
        ...     print(arc.getNom())
        ...
        A->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        if self.getNodesWays(noeudDepart, noeudArrivee):
            arcsWay = random.choice(
                self.getArcsWays(noeudDepart, noeudArrivee)
            )
        else:
            arcsWay = []

        return arcsWay

    def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe.
        [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ du cycle.

        Remarque
        --------
        S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g2 = Graphe('graphe2')
        >>> g2.setNoeuds([A,B,C])
        >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g2)
        Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> # Il y a 6 cycles partants de A dans ce graphe
        >>> # On les récupère tous en fixant la graine
        >>> random.seed(1)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-5->B
        B-5->C
        C-1->A
        >>> random.seed(2)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-5->B
        B-5->C
        C-8->A
        >>> random.seed(6)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-2->B
        B-5->C
        C-1->A
        >>> random.seed(4)
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-2->B
        B-5->C
        C-8->A
        >>> random.seed(5)        
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-1->A
        >>> random.seed(24)        
        >>> for arc in g2.getArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-8->A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        arcsCycle = []
        if self.hasCycleFromNoeud(noeudDepart):
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if (self.getNodesWays(noeud, noeudDepart) and
                        noeud not in noeudsPossibles):
                    noeudsPossibles.append(noeud)
            # On choisit aléatoirement un premier noeud parmi ceux
            # qui permettent de revenir à noeudDepart
            noeudAleatoire = random.choice(noeudsPossibles)
            # On ajoute le début du chemin
            arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire))
            # On ajoute le reste du chemin
            arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart))

        return arcsCycle

    def getMinPoidsFromCycle(self, cycle: list) -> int:
        """Renvoie le plus petits poids des arcs contenus dans cycle.

        Paramètres
        ----------
        cycle : list - Une liste d'arcs du graphe.

        Remarque
        --------
        Ici on gère les éventuels cycles parallèles

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> random.seed(None)
        >>> random.seed(9)
        >>> cycle = g.getArcsCycleFromNoeud(A)        
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-3->C
        C-1->A
        >>> g.getMinPoidsFromCycle(cycle)
        1
        >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud
        >>> random.seed(15)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-5->B
        B-5->C
        C-8->A
        >>> g.getMinPoidsFromCycle(cycle)
        5
        >>> random.seed(13)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-2->B
        B-5->C
        C-8->A
        >>> g.getMinPoidsFromCycle(cycle)
        2
        >>> random.seed(17)
        >>> cycle = g.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-2->B
        B-5->C
        C-1->A
        >>> g.getMinPoidsFromCycle(cycle)
        1
        >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud.
        >>> # Mais il peut rester plusieurs cycles partant de A !
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C])
        >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g1.simplification()
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> cycle = g1.getArcsCycleFromNoeud(A)
        >>> for e in cycle:
        ...     print(e.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> g1.getMinPoidsFromCycle(cycle)
        5
        """  # noqa : E501
        pass
        # les éléments de cycle doivent être des instances de la classe Arc
        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        # Un booléen pour tester si tout est OK
        allOk = True
        for arc in cycle:
            assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc"  # noqa : E501
            if not isinstance(arc, Arc):
                allOk = False
                raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}')  # noqa : E501
            assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément."
            if len(cycle) < 1:
                allOk = False
                raise ValueError('Le cycle doit avoir au moins un élément.')
        if allOk:
            poidsMin = cycle[0].getPoids()
            i = 1
            while i < len(cycle):
                if cycle[i].getPoids() <= poidsMin:
                    poidsMin = cycle[i].getPoids()
                i += 1

        return poidsMin

    def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None:
        """Renvoie l'instance du graphe modifiée.
        Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits
        du plus petit poids des arcs.
        
        Sinon le graphe n'est pas modifié.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ du cycle à éliminer.

        Remarque
        --------
        Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> # Ce graphe contient 2 cycles à partir de A
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...
        ====cycle====
        A
        B
        C
        A
        ====cycle====
        A
        C
        A
        >>> # On élimine le premier, il reste le second
        >>> random.seed(7)
        >>> g.eliminerCycleFromNoeud(A)
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...       
        ====cycle====
        A
        C
        A
        >>> # On élimine le second
        >>> g.eliminerCycleFromNoeud(A)
        >>> # Il n'y a plus de cycle partant de A
        >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
        ...     print("====cycle====")
        ...     for noeud in cycle:
        ...         print(noeud.getNom())
        ...
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        if self.hasCycleFromNoeud(noeudDepart):
            cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart)
        minPoids = self.getMinPoidsFromCycle(cycleToReduce)
        for arc in cycleToReduce:
            arc.setPoids(arc.getPoids()-minPoids)
            # On renomme l'arc
            nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
            arc.setNom(nouveauNom)
            if arc.getPoids() == 0:
                self.removeArc(arc)

    def hasArcInverse(self, arcCandidat: Arc) -> bool:
        """Renvoie un booléen selon que le graphe contient un arc inverse de
        arcCandidat ou pas.

        Paramètres
        ----------
        arcCandidat  : Arc - L'arc dont on veut savoir si il a un arc inverse.

        Remarques
        ---------
        - L'arc inverse n'est pas forcément un arc inverse dans le sens que
        l'arc d'un cycle de Klein pour être un arc inverse introduit au
        debut de l'algorithme.
        - On suppose que le graphe est simplifié au préalable afin qu'il n'y
        ait qu'un seul arc entre deux noeuds dans un sens donné.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.hasArcInverse(AB)
        False
        >>> # On ajoute tous les arcs inverses au graphe
        >>> g.addArcsInversesNuls()
        >>> g.hasArcInverse(AB)
        True
        """
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

        boolDeSortie = False
        if not arcCandidat.getEstInverse():
            for arc in self.getArcsInverses():
                if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                        arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                    boolDeSortie = True
        if arcCandidat.getEstInverse():
            for arc in self.getArcsDirects():
                if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                        arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                    boolDeSortie = True

        return boolDeSortie

    def getArcInverse(self, arcCandidat: Arc) -> Arc:
        """Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.

        Paramètres
        ----------
        arcCandidat  : Arc - L'arc dont on veut récupérer l'arc inverse.

        Remarques
        ---------
        - L'arc inverse n'est pas forcément un arc inverse dans le sens que
        l'arc d'un cycle de Klein peut être un arc inverse introduit au
        debut de l'algorithme.
        - On suppose que le graphe est simplifié au préalable afin qu'il n'y
        ait qu'un seul arc entre deux noeuds dans un sens donné.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> # On ajoute tous les arcs inverses au graphe
        >>> g.addArcsInversesNuls()
        >>> print(AB.getNom())
        A->B
        >>> print(g.getArcInverse(AB).getNom())
        B-0->A
        """
        pass
        assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501
        assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse."  # noqa : E501

        arcInverse = None
        if self.hasArcInverse(arcCandidat):
            if not arcCandidat.getEstInverse():
                for arc in self.getArcsInverses():
                    if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                            arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                        arcInverse = arc
            if arcCandidat.getEstInverse():
                for arc in self.getArcsDirects():
                    if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                            arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                        arcInverse = arc

        return arcInverse

    def addArcsInversesNuls(self) -> None:
        """Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.

        Remarque
        --------
        Si le graphe n'est pas simplifié, on le simplifie d'abord.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> for arc in g.getArcs():
        ...     print(arc)
        ...
        Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
        Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False)
        Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False)
        Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
        Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
        Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False)
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc)
        ...
        Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False)
        Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False)
        Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False)
        Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False)
        Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
        Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
        Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True)
        Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
        """  # noqa : E501
        pass
        assert self.hasArc(), "Le graphe doit avoir des arcs."
        if not self.estSimplifie():
            self.simplification()
        newArcs = []
        for arc in self.getArcs():
            newName = "{}-0->{}".format(
                arc.getNoeudFin().getNom(),
                arc.getNoeudDebut().getNom()
            )
            newArcInverseNul = Arc(
                newName,
                arc.getNoeudFin(),
                arc.getNoeudDebut(),
                0,
                True
            )
            newArcs.append(newArcInverseNul)
        for arc in newArcs:
            self.addArc(arc)

# ================================================================
# =========== METHODES SPECIFIQUES ALGO KLEIN ====================
# ================================================================

    def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
        """Renvoie un booléen selon que le graphe présente un cycle de Klein
        ou non à partir du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Remarque
        --------
        Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> AB.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> BA.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        True
        >>> DE.setPoids(0)
        >>> g.kleinHasCycleFromNoeud(A)
        False
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

        # On récupère tous les noeuds situés à un arc de noeudCancidat
        noeudDirectementAccessibles = self.getCreanciers(noeudCandidat)
        boolDeSortie = False
        for noeud in noeudDirectementAccessibles:
            if self.kleinArcsBetween(noeudCandidat, noeud):
                if self.kleinAllArcsWays(noeud, noeudCandidat):
                    boolDeSortie = True
        return boolDeSortie

    def kleinHasCycle(self) -> None:
        """Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.

        Remarque
        --------
        Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
        >>> g.kleinHasCycle()
        True
        >>> AB.setPoids(0)
        >>> g.kleinHasCycle()
        True
        >>> BA.setPoids(0)
        >>> g.kleinHasCycle()
        True
        >>> DE.setPoids(0)
        >>> g.kleinHasCycle()
        False
        """  # noqa : E501
        pass
        boolDeSortie = False
        index = 0
        noeudsDuGraphe = self.getNoeuds()
        while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and
                index < len(noeudsDuGraphe)-1):
            index += 1
        if index < len(noeudsDuGraphe)-1:
            boolDeSortie = True
        return boolDeSortie

    def kleinArcsBetween(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

        Remarque
        --------
        Méthode utile pour l'algorithme de Klein

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
        >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
        >>> DEg = Arc('D->E',D,E,1)
        >>> EAg = Arc('E->A',E,A,1)
        >>> g.setNoeuds([A,B,D,E])
        >>> g.setArcs([AB1,AB2,AB3,DEg,EAg])
        >>> for a in g.kleinArcsBetween(A,B):
        ...     print(a.getNom())
        ...        
        A->B(2)
        A->B(3)
        >>> # Aucun arc de D à A
        >>> for a in g.kleinArcsBetween(D,A):
        ...     print(a.getNom())
        ...
        >>> g1 = Graphe('graphe1')
        >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1')
        >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1)
        >>> ABbis = Arc('A1->B1(2)',A1,B1,2)
        >>> CB = Arc('C1->B1',C1,B1,1)
        >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1)
        >>> EA = Arc('E1->A1',E1,A1,1)
        >>> EB = Arc('E1->B1',E1,B1,1)
        >>> g1.setNoeuds([A1,B1,C1,D1,E1])
        >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB])
        >>> # print(g1.kleinArcsBetween(A1,B1))
        >>> for a in g1.kleinArcsBetween(A1,B1):
        ...     print(a.getNom())
        ...
        A1->B1
        A1->B1(2)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

        arcs = []
        for arc in self.getArcs():
            if (arc.getNoeudDebut() == noeudDepart and
                    arc.getNoeudFin() == noeudArrivee and
                    arc.getPoids() != 0):
                arcs.append(arc)
        return arcs

    def kleinAllArcsWays(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie une liste de tuples :

         - d'arcs de poids non nul         
         - et/ou d'arcs inverses de poids non nul         
         - ne passant pas deux fois par le même noeud
        contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs des chemins.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.

        Remarques
        ---------
        - Ne trouve pas les cycles.        
        - Méthode utile pour l'algorithme de Klein.
        - On ne peut pas supprimer de chemins ici en amont de la recherche de cycles

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> ABi = Arc('A->B(i)',A,B,1,True)                
        >>> BA1 = Arc('B->A(1)',B,A,0)
        >>> BAi = Arc('B->A(i)',B,A,2,True)        
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
        >>> g.estSimplifie()
        True
        >>> # Deux chemins de Klein de B à C
        >>> # Mais on ne veut que les chemins :
        >>> # --> qui ont des arcs directs de poids non nul
        >>> # --> et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse qu'un
        >>> for chemin in g.kleinAllArcsWays(B,C):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        B->A(i)
        A->C
        >>> # Dans ce graphe cinq chemins sont possibles entre A et B
        >>> # Mais on ne veut que les chemins :
        >>> # --> qui ont des arcs directs de poids non nul
        >>> # --> et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse que quatre
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins
        >>> AB1.setPoids(1)
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
        >>> # Le chemin doit être supprimé des possibles aussi
        >>> # Cela laisse quatre chemins valides
        >>> EB.setPoids(0)
        >>> for chemin in g.kleinAllArcsWays(A,B):
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->B
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
        assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

        # Si il existe au moins un chemin entre noeudDepart et noeudArrivee
        # On ne garde que les chemins :
        #   -> dont les arcs ont des poids non nuls
        #   -> dont les arcs inverses ont des poids non nuls
        #   -> ne passant pas deux fois par le même noeud
        arcsWays = self.getArcsWays(noeudDepart, noeudArrivee)
        if arcsWays:
            # les chemins à garder, a priori vide
            arcsWaysOk = []
            for arcsWay in arcsWays:
                wayToKeep = True
                noeudsConnus = [noeudDepart]
                for arc in arcsWay:
                    if (arc.getPoids() == 0 or
                            arc.getNoeudFin() in noeudsConnus):
                        wayToKeep = False
                    if wayToKeep:
                        noeudsConnus.append(arc.getNoeudFin())
                noeudsConnus = []
                if wayToKeep:
                    arcsWaysOk.append(arcsWay)
        else:
            arcsWaysOk = []

        return arcsWaysOk

    def kleinArcsWay(
        self,
        noeudDepart: Noeud,
        noeudArrivee: Noeud
    ) -> list:
        """Renvoie une liste : 
        
        - d'arcs de poids non nul
        - et/ou d'arcs inverses de poids non nul
        - ne passant pas deux fois par le même noeud

        constituant un chemin entre noeudDepart et noeudArrivee s'il en existe.
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
        - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

        Remarque
        --------
        Ne trouve pas les cycles.        
        Méthode utile pour l'algorithme de Klein.

        noeudDepart  : Noeud - noeud de départ
        noeudArrivee : Noeud - noeud d'arrivée

        Tests
        ----- 
        >>> g = Graphe('graphe')
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> ABi = Arc('A->B(i)',A,B,1,True)                
        >>> BA1 = Arc('B->A(1)',B,A,0)
        >>> BAi = Arc('B->A(i)',B,A,2,True)        
        >>> CB = Arc('C->B',C,B,1)
        >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
        >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
        >>> g.estSimplifie()
        True
        >>> # Un seul chemin de Klein de B à C
        >>> for chemin in g.kleinAllArcsWays(B,C):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        B->A(i)
        A->C
        >>> # Dans ce graphe cinq chemins sont possibles entre A et B
        >>> # Mais on ne veut que les chemins avec
        >>> # des arcs directs de poids non nul        
        >>> # et/ou des arcs inverses de poids non nul
        >>> # Cela n'en laisse que quatre
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On fixe la graine pour en avoir un au hasard
        >>> random.seed(1)             
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())        
        A->C
        C->B
        >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins
        >>> AB1.setPoids(1)        
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->E
        E->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On fixe la graine pour en avoir un au hasard
        >>> random.seed(1)
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())
        A->B(i)
        >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
        >>> # Le chemin doit être supprimé des possibles aussi
        >>> # Cela laisse quatre cemins valides        
        >>> EB.setPoids(0)
        >>> for chemin in g.kleinAllArcsWays(A,B):        
        ...     print("=== chemin de Klein ===")
        ...     for arc in chemin:
        ...         print(arc.getNom())
        ...
        === chemin de Klein ===
        A->B(1)
        === chemin de Klein ===
        A->B(i)
        === chemin de Klein ===
        A->C
        C->B
        === chemin de Klein ===
        A->D
        D->B
        >>> # On a fixé la graine pour en avoir un au hasard
        >>> for arc in g.kleinArcsWay(A,B):
        ...     print(arc.getNom())
        A->B(1)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
        assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
        assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

        arcsWayOk = []
        kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee)
        if kleinArcsWays:
            arcsWayOk = random.choice(kleinArcsWays)

        return arcsWayOk

    def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe
        
        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs des cycles.
        
        Remarques
        ---------
        - Le graphe constitué par les arcs directs doit être simplifié.
        - La méthode kleinAllArcsWays() exclut déjà les chemin passant
        deux fois par le même noeud.
        - On va ici encore exclure certains chemins cycliques à savoir :
            - ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
            - ceux qui n'ont que des arcs inverses
            - ceux qui ont un poids algébrique négatif.
        - Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.

        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g.estSimplifie()
        True
        >>> # Il y a 2 cycles partants de A dans ce graphe
        >>> # Ce sont aussi des cycles de Klein                
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-3->C
        C-9->A
        >>> # On ajoute les arcs inverses manuellement pour les tests
        >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
        >>> g.addArcs([BAi,CAi,CBi,ACi])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-7->B
        B-5->C
        C-1->A(i)
        === cycle ===
        A-3->C
        C-9->A
        >>> # Tests sur un autre graphe
        >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G')
        >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4)
        >>> # On ajoute un arc inverse manuellement
        >>> GFi = Arc('G-3->F(i)',G,F,3,True)
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([D,E,F,G])
        >>> g1.setArcs([DE,EF,FG,FD,GFi])
        >>> # Ne présente qu'un cycle DEFD, 
        >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays()
        >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        D-1->E
        E-2->F
        F-4->D
        >>> # Quelques arcs en plus qui pourraient poser problème
        >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6)
        >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True)
        >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi])
        >>> # Il y a 2 cycles valides à partir de D
        >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        D-1->E
        E-2->F
        F-4->D
        === cycle ===
        D-1->E
        E-6->D
        """  # noqa : E501
        pass

        # On récupère tous les chemins cycliques
        allArcsKleinCycles = []
        if self.kleinHasCycleFromNoeud(noeudDepart):
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if (self.kleinAllArcsWays(noeud, noeudDepart) and
                        noeud not in noeudsPossibles):
                    noeudsPossibles.append(noeud)
            # Pour tous les noeuds qui permettent de revenir à noeudDepart
            for noeudPossible in noeudsPossibles:
                arcsKleinCycle = []
                # Pour tous les chemins entre un noeudPossible et noeudDepart
                suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501
                for chemin in suiteChemin:
                    # pour tous les arcs qui permettent de joindre noeudDepart
                    # et noeudPossible
                    debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501
                    for premierArc in debutChemin:
                        # On remet à zéro
                        arcsKleinCycle = []
                        # On ajoute le premier arc
                        arcsKleinCycle.extend([premierArc])
                        # On ajoute le reste du chemin
                        arcsKleinCycle.extend(chemin)
                        # On évite les doublons
                        if arcsKleinCycle not in allArcsKleinCycles:
                            allArcsKleinCycles.append(arcsKleinCycle)

            # Il faut encore nettoyer de certains chemins :
            #   -> ceux qui n'ont que deux arcs qui sont inverses
            #     l'un de l'autre
            #   -> ceux qui n'ont que des arcs inverses
            #   -> ceux qui ont un poids algébrique négatif.
            # ======== Remarque
            # On a besoin d'un variable globale pour éviter l'erreur
            # UnboundLocalError: local variable referenced before assignment python # noqa : E501
            # ========
            global allArcsKleinCyclesCleaned
            allArcsKleinCyclesCleaned = []
            for arcsKleinCycle in allArcsKleinCycles:
                cycleToKeep = True
                # ceux qui n'ont que deux arcs qui sont inverses
                # l'un de l'autre
                if len(arcsKleinCycle) == 2:
                    arc0 = arcsKleinCycle[0]
                    arc1 = arcsKleinCycle[1]
                    if self.hasArcInverse(arc1):
                        if arc0 == self.getArcInverse(arc1):
                            cycleToKeep = False
                    if self.hasArcInverse(arc0):
                        if arc1 == self.getArcInverse(arc0):
                            cycleToKeep = False
                # ceux qui n'ont que des arcs inverses
                onlyArcsInverses = True
                for arc in arcsKleinCycle:
                    if not arc.getEstInverse():
                        onlyArcsInverses = False
                if onlyArcsInverses:
                    cycleToKeep = False
                # ceux qui ont un poids algébrique négatif
                cycleWeight = 0
                for arc in arcsKleinCycle:
                    if arc.getEstInverse():
                        cycleWeight -= arc.getPoids()
                    if not arc.getEstInverse():
                        cycleWeight += arc.getPoids()
                if cycleWeight <= 0:
                    cycleToKeep = False

                if cycleToKeep:
                    allArcsKleinCyclesCleaned.append(arcsKleinCycle)

        return allArcsKleinCyclesCleaned

    def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.

        Renvoie [] sinon.

        Paramètres
        ----------
        - noeudDepart  : Noeud - Le noeud de départ des arcs du cycle.

        Remarques
        ---------
        - S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
        - La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs
        parallèles entre deux noeuds soient fucionnés.
        - Un cycle ne doit pas passer plusieurs fois par le même noeud


        Tests
        -----
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
        >>> g.estSimplifie()
        False
        >>> g.simplification()
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        >>> g.estSimplifie()
        True
        >>> # Il y a 2 cycles partants de A dans ce graphe
        >>> # Ce sont aussi des cycles de Klein
        >>> # On les récupère tous en fixant la graine
        >>> random.seed(1)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> random.seed(5)        
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...        
        A-3->C
        C-9->A
        >>> # On ajoute les arcs inverses manuellement pour les tests
        >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
        >>> g.addArcs([BAi,CAi,CBi,ACi])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
        >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle ===
        A-7->B
        B-5->C
        C-9->A
        === cycle ===
        A-7->B
        B-5->C
        C-1->A(i)
        === cycle ===
        A-3->C
        C-9->A
        >>> random.seed(1)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-9->A
        >>> random.seed(5)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-3->C
        C-9->A
        >>> random.seed(7)
        >>> for arc in g.kleinArcsCycleFromNoeud(A):
        ...     print(arc.getNom())
        ...
        A-7->B
        B-5->C
        C-1->A(i)
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

        arcsKleinCycle = []
        if self.kleinHasCycleFromNoeud(noeudDepart):
            allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart)
            arcsKleinCycle = random.choice(allCycles)

        return arcsKleinCycle

    def kleinAllArcsCycles(self) -> list:
        """Renvoie tous les cycles d'un graphe sans doublons.

        Remarques
        ---------
        - Notamment deux cycles sont considérés comme identiques s'ils
        contiennent globalement les mêmes arcs.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
        >>> # Mais 6 cycles distincts
        >>> print(len(G3.kleinAllArcsCycles()))
        6
        >>> for cycle in G3.kleinAllArcsCycles():
        ...     print('=== cycle unique ===')
        ...     for arc in cycle:
        ...         print(arc.getNom())
        ...
        === cycle unique ===
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        === cycle unique ===
        A-4->B
        B-2->E
        E-7->F
        F-5->A
        === cycle unique ===
        A-2->E
        E-7->F
        F-5->A
        === cycle unique ===
        B-6->C
        C-5->D
        D-2->B
        === cycle unique ===
        B-2->E
        E-7->F
        F-3->C
        C-5->D
        D-2->B
        === cycle unique ===
        C-5->D
        D-4->E
        E-7->F
        F-3->C
        """
        pass

        # On récupère tous les noeuds
        noeuds = self.getNoeuds()
        # On récupère les cycles au fur et à mesure
        cyclesSansDoublons = []
        # On initialise avec tous les cycles du premier noeud
        for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]):
            cyclesSansDoublons.append(cycle)
        # Pour chaque noeuds
        for noeud in noeuds:
            # Pour chaque cycle partant du noeud
            for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
                toAdd = True
                for cycleUnique in cyclesSansDoublons:
                    if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501
                        toAdd = False
                if toAdd:
                    cyclesSansDoublons.append(cycle)

        return cyclesSansDoublons

    def kleinEstCycle(self, arcsCycle: list) -> bool:
        """Renvoie un booléen selon que arcsCycle est un cycle de Klein du
        graphe ou non.

        Remarque
        --------
        On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut
        les doublons or tous les cycles doivent être testés par exemple
        ABCA et BCAB sont un doublon du même cycle mais doivent être testés
        tous les deux.

        Paramètres
        ----------
        arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
        >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)]
        >>> G3.kleinEstCycle(cycleOK)
        True
        >>> G3.kleinEstCycle(cycleKO)
        False
        """
        pass
        for arc in arcsCycle:
            assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

        # booléen de sortie
        boolDeSortie = True
        # On récupère tous les cycles du graphe
        cyclesDuGraphe = []
        for noeud in self.getNoeuds():
            for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
                cyclesDuGraphe.append(cycle)
        if arcsCycle not in cyclesDuGraphe:
            boolDeSortie = False

        return boolDeSortie

    def kleinModifierCycle(self, arcsCycle: list) -> None:
        """Renvoie le graphe modifié après réduction du cycle.

        Remarque
        --------
        - Chaque arc dans le sens du cycle est modifié en diminuant son poids
        du plus petit poids des arcs du cycle.
        - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
        son poids du plus petit poids des arcs du cycle.

        Paramètres
        ----------
        arcsCycle : list - Le cycle de Klein à réduire.

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> G3.addArcsInversesNuls()
        >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
        >>> for arc in cycleOK:
        ...     print(arc.getNom())
        ...
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        >>> G3.kleinModifierCycle(cycleOK)
        >>> for arc in cycleOK:
        ...     print(arc.getNom())
        ...
        A-0->B
        B-2->C
        C-1->D
        D-0->E
        E-3->F
        F-1->A
        """ # noqa : E501
        pass
        assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501
        for arc in arcsCycle:
            assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

        minPoids = self.getMinPoidsFromCycle(arcsCycle)
        for arc in arcsCycle:
            # On diminue le poids de l'arc
            arc.setPoids(arc.getPoids()-minPoids)
            # On augmente le poids de son arc inverse
            arcInverse = self.getArcInverse(arc)
            arcInverse.setPoids(arcInverse.getPoids()+minPoids)
            # On renomme les arcs
            nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
            arc.setNom(nouveauNom)
            nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom()  # noqa : E501
            arcInverse.setNom(nouveauNomInverse)

    def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None:
        """Modifie un cycle de Klein pour un noeud du graphe s'il en existe,
        sinon le graphe n'est pas modifié.

        - Chaque arc dans le sens du cycle est modifié en diminuant son poids
        du plus petit poids des arcs du cycle.
        - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
        son poids du plus petit poids des arcs du cycle.

        Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un
        cycle de Klein.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Remarque
        --------
        - Un cycle de Klein est un cycle :
            - dont tous les arcs ont un poids non nul.        
            - dont le poids algébrique est strictement positif.
        - Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-0->C => arc inverse :  True
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ACA
        >>> random.seed(11)
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-0->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-6->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-3->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-3->C => arc inverse :  True
        >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse
        >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
        ...     print("=== cycle ===")
        ...     for arc in cycle:
        ...         print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        === cycle ===
        A-7->B => arc inverse :  False
        B-5->C => arc inverse :  False
        C-6->A => arc inverse :  False
        === cycle ===
        A-7->B => arc inverse :  False
        B-5->C => arc inverse :  False
        C-3->A => arc inverse :  True
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501
        # Pour chaque arc du cycle, il doit y avoir un arc inverse
        for arc in self.getArcs():
            assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse"  # noqa : E501

        if self.kleinHasCycleFromNoeud(noeudCandidat):
            # On choisit un cycle à réduire au hasard
            cycleToChange = random.choice(
                self.kleinAllArcsCyclesFromNoeud(noeudCandidat)
            )
            self.kleinModifierCycle(cycleToChange)

    def kleinDeleteDettesNulles(self) -> None:
        """Supprime tous les arcs de poids nul.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> random.seed(1)
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ABCA
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-0->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs de poids nul
        >>> g.kleinDeleteDettesNulles()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        """  # noqa : E501
        pass

        # On récupère d'abord tous les arcs à supprimer
        # Car la suppression de proche en proche peut laisser
        # un arc à l'écart à cause de la modification du graphe
        arcsToDelete = []
        for arc in self.getArcs():
            if arc.getPoids() == 0:
                arcsToDelete.append(arc)
        # On supprime alors tous les arcs à supprimer
        for arc in arcsToDelete:
            self.removeArc(arc)

    def kleinDeleteArcsInverses(self) -> None:
        """Supprime tous les arcs inverses temporaires.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
        >>> # On simplifie le graphe
        >>> g.simplification()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        >>> # On ajoute les arcs inverses
        >>> g.addArcsInversesNuls()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-7->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-5->C => arc inverse :  False
        C-9->A => arc inverse :  False
        B-0->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-0->B => arc inverse :  True
        A-0->C => arc inverse :  True
        >>> random.seed(1)
        >>> # Ce graphe présente deux cycles : ABCA et ACA
        >>> # On modifie ABCA
        >>> g.kleinModifierCycleFromNoeud(A)
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        B-0->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-0->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs de poids nul
        >>> g.kleinDeleteDettesNulles()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        B-5->A => arc inverse :  True
        C-5->B => arc inverse :  True
        A-5->C => arc inverse :  True
        >>> # On supprime les arcs inverses temporaires
        >>> g.kleinDeleteArcsInverses()
        >>> for arc in g.getArcs():
        ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
        ...
        A-2->B => arc inverse :  False
        A-3->C => arc inverse :  False
        C-4->A => arc inverse :  False
        """  # noqa : E501
        pass

        # On récupère d'abord tous les arcs à supprimer
        # Car la suppression de proche en proche peut laisser
        # un arc à l'écart à cause de la modification du graphe
        arcsToDelete = []
        for arc in self.getArcs():
            if arc.getEstInverse():
                arcsToDelete.append(arc)
        # On supprime alors tous les arcs à supprimer du graphe
        for arc in arcsToDelete:
            self.removeArc(arc)

# ================================================================
# =========== METHODES NON UTILISÉES =============================
# ================================================================
    def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list:
        """Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe.
        [] sinon.

        Paramètres
        ----------
        noeudDepart  : Noeud - Le noeud de départ du cycle.

        Remarque
        --------
        S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.

        Tests
        -----
        >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
        >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BA = Arc('B->A',B,A,1)
        >>> CB = Arc('C->B',C,B,1)
        >>> DE = Arc('D->E',D,E,2)
        >>> EA = Arc('E->A',E,A,3)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E])
        >>> g.setArcs([AB,AC,AD,CB,DE,EA])
        >>> print(g)
        Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
        >>> for e in g.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        D
        E
        A
        >>> g.getNodesCycleFromNoeud(B)
        []
        >>> g1 = Graphe('graphe1')
        >>> g1.setNoeuds([A,B,C,D,E])
        >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
        >>> print(g1)
        Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
        >>> # Il y a deux cycles partants de A dans ce graphe
        >>> random.seed(3)
        >>> for e in g1.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        B
        A
        >>> random.seed(7)
        >>> for e in g1.getNodesCycleFromNoeud(A):
        ...     print(e.getNom())
        ...
        A
        C
        B
        A
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
        cycle = []
        if self.hasCycleFromNoeud(noeudDepart):
            cycle.append(noeudDepart)
            noeudsPossibles = []
            for noeud in self.getCreanciers(noeudDepart):
                if self.getNodesWays(noeud, noeudDepart):
                    noeudsPossibles.append(noeud)
            noeudAleatoire = random.choice(noeudsPossibles)
            cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart)
            cheminAleatoire = random.choice(cheminsPossibles)
            cycle.extend(cheminAleatoire)

        return cycle

    def kleinBestArcsCycle(self) -> list:
        """Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.

        Remarques
        ---------
        - Il semble qu'en choisissant à chaque étape le cycle qui réduit
        au maximum la dette globale, la réduction finale ne soit pas la
        meilleure.
        - Cette fonction est donc incorrecte

        Tests
        -----
        >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
        >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
        >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
        >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
        >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
        >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
        >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
        >>> G3 = Graphe('graphe3')
        >>> G3.setNoeuds([A, B, C, D, E, F])
        >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
        >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
        >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
        >>> # Mais 6 cycles distincts
        >>> print(len(G3.kleinAllArcsCycles()))
        6
        >>> G3.addArcsInversesNuls()
        >>> bestCycle1 = G3.kleinBestArcsCycle()
        >>> for arc in bestCycle1:
        ...     print(arc.getNom())
        ...
        A-4->B
        B-6->C
        C-5->D
        D-4->E
        E-7->F
        F-5->A
        >>> G3.kleinModifierCycle(bestCycle1)
        >>> bestCycle2 = G3.kleinBestArcsCycle()
        >>> for arc in bestCycle2:
        ...     print(arc.getNom())
        ...
        B-2->E
        E-3->F
        F-3->C
        C-4->B
        """
        pass
        # On récupère tous les cycles sans doublons
        allCycles = self.kleinAllArcsCycles()
        bestCycle = []
        reductionDeLaDette = 0
        for cycle in allCycles:
            reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle)
            if reductionDuCycle > reductionDeLaDette:
                reductionDeLaDette = reductionDuCycle
                bestCycle = []
                bestCycle.extend(cycle)

        return bestCycle

    def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple:
        """Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur :
        - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi
        - 2e  : Clef : nom d'un noeud - Valeur : couleur du noeud

        blanc : non visité
        gris  : en cours de visite
        noir  : visité

        Remarques
        ---------
        - J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir.
        Toutefois je ne veux pas perdre le code pour le moment.
        - La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement
        pour qu'elle traite tout le graphe.
        - L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
        - À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris
        tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc
        accessible on dépile.

        Tests
        -----
        >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
        >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H')
        >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1)
        >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1)
        >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1)
        >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1)
        >>> FH = Arc('F->H',F,H,1)
        >>> GE = Arc('G->E',G,E,1)
        >>> HB = Arc('H->B',H,B,1)
        >>> g = Graphe('graphe')
        >>> g.setNoeuds([A,B,C,D,E,F,G,H])
        >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB])
        >>> # Dictionnaire des pères à partir de D
        >>> g.colorierGrapheFromNoeud(D)[0]
        {'D': None, 'E': 'D', 'G': 'D'}
        >>> # Dictionnaire du coloriage partir de D
        >>> g.colorierGrapheFromNoeud(D)[1]
        {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'}
        >>> # Dictionnaire des pères à partir de C
        >>> g.colorierGrapheFromNoeud(C)[0]
        {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'}
        >>> # Dictionnaire du coloriage partir de C
        >>> g.colorierGrapheFromNoeud(C)[1]
        {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
        """  # noqa : E501
        pass
        assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

        pile = []
        parents = {}
        couleurs = {}
        for noeud in self.getNoeuds():
            couleurs[noeud.getNom()] = 'blanc'
        parents[noeudDepart.getNom()] = None
        couleurs[noeudDepart.getNom()] = 'gris'
        pile.append(noeudDepart)
        while pile != []:
            pere = pile[-1]
            descendants = []
            for noeud in self.getCreanciers(pere):
                if couleurs[noeud.getNom()] == 'blanc':
                    descendants.append(noeud)
            if descendants != []:
                fils = descendants[0]
                couleurs[fils.getNom()] = 'gris'
                parents[fils.getNom()] = pere.getNom()
                pile.append(fils)
            else:
                pile.pop()
                couleurs[pere.getNom()] = 'noir'
        return parents, couleurs

Methods

def addArc(self, arcCandidat: models.arc.Arc) ‑> None

Ajoute une instance de la classe Arc au graphe.

Paramètres

arcCandidat : Arc - Un arc à ajouter au graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
    >>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[])
>>> arc = Arc('n1n2', n1, n2, 7)
>>> g1.addArc(arc)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
Expand source code
def addArc(self, arcCandidat: Arc) -> None:
    """Ajoute une instance de la classe Arc au graphe.

    Paramètres
    ----------
    arcCandidat : Arc - Un arc à ajouter au graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=[], arcs=[])
        >>> n1 = Noeud('n1')
    >>> n2 = Noeud('n2')
    >>> g1.addNoeud(n1)
    >>> g1.addNoeud(n2)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[])
    >>> arc = Arc('n1n2', n1, n2, 7)
    >>> g1.addArc(arc)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
    """
    pass
    assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
    assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe."  # noqa : E501
    nomsDesArcsDuGraphe = []
    for arc in self.getArcs():
        nomsDesArcsDuGraphe.append(arc.nom)
    assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

    # Un booléen pour tester si tout est OK
    allOk = True
    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if arcCandidat in self.getArcs():
        allOk = False
        raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.')  # noqa : E501
    if arcCandidat.nom in nomsDesArcsDuGraphe:
        allOk = False
        raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
    if allOk:
        self.arcs.append(arcCandidat)
def addArcs(self, arcsToAdd: list) ‑> None

Ajoute une liste d'instances de la classe Arc au graphe.

Paramètres

arcsToAdd : list - Une liste d'arcs à ajouter au graphe.

Tests

>>> g = Graphe('graphe')
>>> print(g)
Graphe(nom=graphe, noeuds=[], arcs=[])
>>> A,B = Noeud('A'), Noeud('B')
>>> g.setNoeuds([A,B])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[])
>>> AB1 = Arc('A->B(1)', A, B, 7)
>>> g.addArc(AB1)
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)'])
>>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7)
>>> g.addArcs([AB2,AB3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
Expand source code
def addArcs(self, arcsToAdd: list) -> None:
    """Ajoute une liste d'instances de la classe Arc au graphe.

    Paramètres
    ----------
    arcsToAdd : list - Une liste d'arcs à ajouter au graphe.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> print(g)
    Graphe(nom=graphe, noeuds=[], arcs=[])
    >>> A,B = Noeud('A'), Noeud('B')
    >>> g.setNoeuds([A,B])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[])
    >>> AB1 = Arc('A->B(1)', A, B, 7)
    >>> g.addArc(AB1)
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)'])
    >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7)
    >>> g.addArcs([AB2,AB3])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
    """ # noqa : E501
    pass
    for arc in arcsToAdd:
        assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501

    for arc in arcsToAdd:
        self.addArc(arc)
def addArcsInversesNuls(self) ‑> None

Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.

Remarque

Si le graphe n'est pas simplifié, on le simplifie d'abord.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> for arc in g.getArcs():
...     print(arc)
...
Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False)
Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False)
Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False)
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
...     print(arc)
...
Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False)
Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False)
Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False)
Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False)
Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True)
Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
Expand source code
def addArcsInversesNuls(self) -> None:
    """Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.

    Remarque
    --------
    Si le graphe n'est pas simplifié, on le simplifie d'abord.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> for arc in g.getArcs():
    ...     print(arc)
    ...
    Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
    Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False)
    Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False)
    Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
    Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
    Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False)
    >>> g.addArcsInversesNuls()
    >>> for arc in g.getArcs():
    ...     print(arc)
    ...
    Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False)
    Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False)
    Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False)
    Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False)
    Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
    Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
    Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True)
    Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
    """  # noqa : E501
    pass
    assert self.hasArc(), "Le graphe doit avoir des arcs."
    if not self.estSimplifie():
        self.simplification()
    newArcs = []
    for arc in self.getArcs():
        newName = "{}-0->{}".format(
            arc.getNoeudFin().getNom(),
            arc.getNoeudDebut().getNom()
        )
        newArcInverseNul = Arc(
            newName,
            arc.getNoeudFin(),
            arc.getNoeudDebut(),
            0,
            True
        )
        newArcs.append(newArcInverseNul)
    for arc in newArcs:
        self.addArc(arc)
def addNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> None

Ajoute une instance de la classe Noeud au graphe.

Paramètres

noeudCandidat : Noeud - Un noeud à ajouter au graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> g1.addNoeud(n1)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
Expand source code
def addNoeud(self, noeudCandidat: Noeud) -> None:
    """Ajoute une instance de la classe Noeud au graphe.

    Paramètres
    ----------
    noeudCandidat : Noeud - Un noeud à ajouter au graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=[], arcs=[])
    >>> n1 = Noeud('n1')
    >>> g1.addNoeud(n1)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
    """
    pass
    assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
    assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe."  # noqa : E501
    nomsDesNoeudsDuGraphe = []
    for noeud in self.noeuds:
        nomsDesNoeudsDuGraphe.append(noeud.nom)
    assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom."  # noqa : E501

    # Un booléen pour tester si tout est OK
    allOk = True
    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if self.isNoeudInGraphe(noeudCandidat):
        allOk = False
        raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.')  # noqa : E501
    if noeudCandidat.nom in nomsDesNoeudsDuGraphe:
        allOk = False
        raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.')  # noqa : E501
    if allOk:
        self.noeuds.append(noeudCandidat)
def colorierGrapheFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> tuple

Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur : - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi - 2e : Clef : nom d'un noeud - Valeur : couleur du noeud

blanc : non visité gris : en cours de visite noir : visité

Remarques

  • J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir. Toutefois je ne veux pas perdre le code pour le moment.
  • La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement pour qu'elle traite tout le graphe.
  • L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
  • À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc accessible on dépile.

Tests

>>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
>>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H')
>>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1)
>>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1)
>>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1)
>>> FH = Arc('F->H',F,H,1)
>>> GE = Arc('G->E',G,E,1)
>>> HB = Arc('H->B',H,B,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E,F,G,H])
>>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB])
>>> # Dictionnaire des pères à partir de D
>>> g.colorierGrapheFromNoeud(D)[0]
{'D': None, 'E': 'D', 'G': 'D'}
>>> # Dictionnaire du coloriage partir de D
>>> g.colorierGrapheFromNoeud(D)[1]
{'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'}
>>> # Dictionnaire des pères à partir de C
>>> g.colorierGrapheFromNoeud(C)[0]
{'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'}
>>> # Dictionnaire du coloriage partir de C
>>> g.colorierGrapheFromNoeud(C)[1]
{'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
Expand source code
def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple:
    """Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur :
    - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi
    - 2e  : Clef : nom d'un noeud - Valeur : couleur du noeud

    blanc : non visité
    gris  : en cours de visite
    noir  : visité

    Remarques
    ---------
    - J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir.
    Toutefois je ne veux pas perdre le code pour le moment.
    - La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement
    pour qu'elle traite tout le graphe.
    - L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
    - À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris
    tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc
    accessible on dépile.

    Tests
    -----
    >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
    >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H')
    >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1)
    >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1)
    >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1)
    >>> FH = Arc('F->H',F,H,1)
    >>> GE = Arc('G->E',G,E,1)
    >>> HB = Arc('H->B',H,B,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C,D,E,F,G,H])
    >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB])
    >>> # Dictionnaire des pères à partir de D
    >>> g.colorierGrapheFromNoeud(D)[0]
    {'D': None, 'E': 'D', 'G': 'D'}
    >>> # Dictionnaire du coloriage partir de D
    >>> g.colorierGrapheFromNoeud(D)[1]
    {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'}
    >>> # Dictionnaire des pères à partir de C
    >>> g.colorierGrapheFromNoeud(C)[0]
    {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'}
    >>> # Dictionnaire du coloriage partir de C
    >>> g.colorierGrapheFromNoeud(C)[1]
    {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

    pile = []
    parents = {}
    couleurs = {}
    for noeud in self.getNoeuds():
        couleurs[noeud.getNom()] = 'blanc'
    parents[noeudDepart.getNom()] = None
    couleurs[noeudDepart.getNom()] = 'gris'
    pile.append(noeudDepart)
    while pile != []:
        pere = pile[-1]
        descendants = []
        for noeud in self.getCreanciers(pere):
            if couleurs[noeud.getNom()] == 'blanc':
                descendants.append(noeud)
        if descendants != []:
            fils = descendants[0]
            couleurs[fils.getNom()] = 'gris'
            parents[fils.getNom()] = pere.getNom()
            pile.append(fils)
        else:
            pile.pop()
            couleurs[pere.getNom()] = 'noir'
    return parents, couleurs
def detteGlobale(self) ‑> int

Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.

Tests

>>> g1 = Graphe('graphe1')
>>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F')
>>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2)
>>> g1.setNoeuds([E,D,F])
>>> g1.setArcs([ED,DF,FE])
>>> g1.detteGlobale()
19
>>> g2 = Graphe('graphe2')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g2.setNoeuds([A,B,C])
>>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> g2.detteGlobale()
24
Expand source code
def detteGlobale(self) -> int:
    """Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F')
    >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2)
    >>> g1.setNoeuds([E,D,F])
    >>> g1.setArcs([ED,DF,FE])
    >>> g1.detteGlobale()
    19
    >>> g2 = Graphe('graphe2')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g2.setNoeuds([A,B,C])
    >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> g2.detteGlobale()
    24
    """  # noqa : E501
    pass
    assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501        

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    allOk = True
    if len(self.getNoeuds()) < 2:
        allOk = False
        raise ValueError("Le graphe doit avoir au moins deux noeuds.")
    if allOk:
        detteGlobale = 0
        for arc in self.getArcs():
            detteGlobale += arc.poids

    return detteGlobale
def eliminerCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> None

Renvoie l'instance du graphe modifiée. Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits du plus petit poids des arcs.

Sinon le graphe n'est pas modifié.

Paramètres

noeudDepart : Noeud - Le noeud de départ du cycle à éliminer.

Remarque

Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> # Ce graphe contient 2 cycles à partir de A
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
...     print("====cycle====")
...     for noeud in cycle:
...         print(noeud.getNom())
...
====cycle====
A
B
C
A
====cycle====
A
C
A
>>> # On élimine le premier, il reste le second
>>> random.seed(7)
>>> g.eliminerCycleFromNoeud(A)
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
...     print("====cycle====")
...     for noeud in cycle:
...         print(noeud.getNom())
...       
====cycle====
A
C
A
>>> # On élimine le second
>>> g.eliminerCycleFromNoeud(A)
>>> # Il n'y a plus de cycle partant de A
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
...     print("====cycle====")
...     for noeud in cycle:
...         print(noeud.getNom())
...
Expand source code
def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None:
    """Renvoie l'instance du graphe modifiée.
    Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits
    du plus petit poids des arcs.
    
    Sinon le graphe n'est pas modifié.

    Paramètres
    ----------
    noeudDepart  : Noeud - Le noeud de départ du cycle à éliminer.

    Remarque
    --------
    Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> g.simplification()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> # Ce graphe contient 2 cycles à partir de A
    >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
    ...     print("====cycle====")
    ...     for noeud in cycle:
    ...         print(noeud.getNom())
    ...
    ====cycle====
    A
    B
    C
    A
    ====cycle====
    A
    C
    A
    >>> # On élimine le premier, il reste le second
    >>> random.seed(7)
    >>> g.eliminerCycleFromNoeud(A)
    >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
    ...     print("====cycle====")
    ...     for noeud in cycle:
    ...         print(noeud.getNom())
    ...       
    ====cycle====
    A
    C
    A
    >>> # On élimine le second
    >>> g.eliminerCycleFromNoeud(A)
    >>> # Il n'y a plus de cycle partant de A
    >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
    ...     print("====cycle====")
    ...     for noeud in cycle:
    ...         print(noeud.getNom())
    ...
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    if self.hasCycleFromNoeud(noeudDepart):
        cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart)
    minPoids = self.getMinPoidsFromCycle(cycleToReduce)
    for arc in cycleToReduce:
        arc.setPoids(arc.getPoids()-minPoids)
        # On renomme l'arc
        nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
        arc.setNom(nouveauNom)
        if arc.getPoids() == 0:
            self.removeArc(arc)
def estSimplifie(self) ‑> bool

Retourne un booléen selon que le graphe est simplifié ou non.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g1 = copy.deepcopy(g)
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g1.estSimplifie()
True
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A,B])
>>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1)
>>> g2.setArcs([AB1,BA1])
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)'])
>>> g2.addArcsInversesNuls()
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B'])
>>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs
>>> g2.estSimplifie()
True
Expand source code
def estSimplifie(self) -> bool:
    """Retourne un booléen selon que le graphe est simplifié ou non.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> g.estSimplifie()
    False
    >>> g1 = copy.deepcopy(g)
    >>> g1.simplification()
    >>> print(g1)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> g1.estSimplifie()
    True
    >>> g2 = Graphe('graphe2')
    >>> g2.setNoeuds([A,B])
    >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1)
    >>> g2.setArcs([AB1,BA1])
    >>> print(g2)
    Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)'])
    >>> g2.addArcsInversesNuls()
    >>> print(g2)
    Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B'])
    >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs
    >>> g2.estSimplifie()
    True
    """  # noqa : E501
    pass
    arcsDirects = self.getArcsDirects()
    estSimplifie = True
    for i in range(len(arcsDirects)):
        k = 0
        for j in range(len(arcsDirects)):
            if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and  # noqa : E501
                    arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()):  # noqa : E501
                k += 1
        if k > 1:
            estSimplifie = False

    return estSimplifie
def getAllNodesCyclesFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie tous les cycles partant du noeudDepart s'il en existe.

Paramètres

noeudDepart : Noeud - Le noeud de départ des cycles.

Tests

>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
...     print("====cycle====")
...     for n in cycle:
...         print(n.getNom())
...
====cycle====
A
D
E
A
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for cycle in g1.getAllNodesCyclesFromNoeud(A):
...     print("====cycle====")
...     for n in cycle:
...         print(n.getNom())
...
====cycle====
A
B
A
====cycle====
A
C
B
A
>>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C')
>>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3)
>>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1)
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A1,B1,C1])
>>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis])
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> # Il y a 6 cycles distincts partant de A1
>>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent.
>>> for cycle in g2.getAllNodesCyclesFromNoeud(A1):
...     print("====cycle====")
...     for n in cycle:
...         print(n.getNom())
...
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
C
A
====cycle====
A
C
A
Expand source code
def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
    """Renvoie tous les cycles partant du noeudDepart s'il en existe.

    Paramètres
    ----------
    noeudDepart  : Noeud - Le noeud de départ des cycles.

    Tests
    -----
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE = Arc('D->E',D,E,2)
    >>> EA = Arc('E->A',E,A,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
    >>> for cycle in g.getAllNodesCyclesFromNoeud(A):
    ...     print("====cycle====")
    ...     for n in cycle:
    ...         print(n.getNom())
    ...
    ====cycle====
    A
    D
    E
    A
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C,D,E])
    >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
    >>> for cycle in g1.getAllNodesCyclesFromNoeud(A):
    ...     print("====cycle====")
    ...     for n in cycle:
    ...         print(n.getNom())
    ...
    ====cycle====
    A
    B
    A
    ====cycle====
    A
    C
    B
    A
    >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C')
    >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3)
    >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1)
    >>> g2 = Graphe('graphe2')
    >>> g2.setNoeuds([A1,B1,C1])
    >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis])
    >>> print(g2)
    Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> # Il y a 6 cycles distincts partant de A1
    >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent.
    >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1):
    ...     print("====cycle====")
    ...     for n in cycle:
    ...         print(n.getNom())
    ...
    ====cycle====
    A
    B
    C
    A
    ====cycle====
    A
    B
    C
    A
    ====cycle====
    A
    B
    C
    A
    ====cycle====
    A
    B
    C
    A
    ====cycle====
    A
    C
    A
    ====cycle====
    A
    C
    A
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    allCycles = []
    if self.hasCycleFromNoeud(noeudDepart):
        for noeud in self.getCreanciers(noeudDepart):
            for i in range(len(self.getNodesWays(noeud, noeudDepart))):
                cycle = []
                cycle = [noeudDepart]
                cycle.extend(self.getNodesWays(noeud, noeudDepart)[i])
                allCycles.append(cycle)
    return allCycles
def getArcInverse(self, arcCandidat: models.arc.Arc) ‑> models.arc.Arc

Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.

Paramètres

arcCandidat : Arc - L'arc dont on veut récupérer l'arc inverse.

Remarques

  • L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein peut être un arc inverse introduit au debut de l'algorithme.
  • On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> # On ajoute tous les arcs inverses au graphe
>>> g.addArcsInversesNuls()
>>> print(AB.getNom())
A->B
>>> print(g.getArcInverse(AB).getNom())
B-0->A
Expand source code
def getArcInverse(self, arcCandidat: Arc) -> Arc:
    """Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.

    Paramètres
    ----------
    arcCandidat  : Arc - L'arc dont on veut récupérer l'arc inverse.

    Remarques
    ---------
    - L'arc inverse n'est pas forcément un arc inverse dans le sens que
    l'arc d'un cycle de Klein peut être un arc inverse introduit au
    debut de l'algorithme.
    - On suppose que le graphe est simplifié au préalable afin qu'il n'y
    ait qu'un seul arc entre deux noeuds dans un sens donné.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
    >>> # On ajoute tous les arcs inverses au graphe
    >>> g.addArcsInversesNuls()
    >>> print(AB.getNom())
    A->B
    >>> print(g.getArcInverse(AB).getNom())
    B-0->A
    """
    pass
    assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501
    assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse."  # noqa : E501

    arcInverse = None
    if self.hasArcInverse(arcCandidat):
        if not arcCandidat.getEstInverse():
            for arc in self.getArcsInverses():
                if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                        arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                    arcInverse = arc
        if arcCandidat.getEstInverse():
            for arc in self.getArcsDirects():
                if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
                        arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
                    arcInverse = arc

    return arcInverse
def getArcs(self) ‑> list

Renvoie le tableau des arcs du graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addNoeud(n3)
>>> a1 = Arc('n1n2_0', n1, n2, 1)
>>> a2 = Arc('n1n2_1', n1, n2, 2)
>>> a3 = Arc('n1n3_0', n1, n3, 3)
>>> a4 = Arc('n2n3_0', n2, n3, 4)
>>> g1.addArc(a1)
>>> g1.addArc(a2)
>>> g1.addArc(a3)
>>> g1.addArc(a4)
>>> for arc in g1.getArcs():
...     # On n'affiche que les noms des arcs
...     print(arc.nom)
...
n1n2_0
n1n2_1
n1n3_0
n2n3_0
>>> for noeud in g1.getNoeuds():
...     print(noeud)
...
Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0)
Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0)
Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
Expand source code
def getArcs(self) -> list:
    """Renvoie le tableau des arcs du graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1 = Noeud('n1')
    >>> n2 = Noeud('n2')
    >>> n3 = Noeud('n3')
    >>> g1.addNoeud(n1)
    >>> g1.addNoeud(n2)
    >>> g1.addNoeud(n3)
    >>> a1 = Arc('n1n2_0', n1, n2, 1)
    >>> a2 = Arc('n1n2_1', n1, n2, 2)
    >>> a3 = Arc('n1n3_0', n1, n3, 3)
    >>> a4 = Arc('n2n3_0', n2, n3, 4)
    >>> g1.addArc(a1)
    >>> g1.addArc(a2)
    >>> g1.addArc(a3)
    >>> g1.addArc(a4)
    >>> for arc in g1.getArcs():
    ...     # On n'affiche que les noms des arcs
    ...     print(arc.nom)
    ...
    n1n2_0
    n1n2_1
    n1n3_0
    n2n3_0
    >>> for noeud in g1.getNoeuds():
    ...     print(noeud)
    ...
    Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0)
    Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0)
    Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
    """
    pass
    return self.arcs
def getArcsBetween(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list

Renvoie tous les arcs entre noeudDepart et noeudArrivee.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs.
  • noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

Tests

>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DE,EA])
>>> for a in g.getArcsBetween(A,B):
...     print(a.getNom())
...
A->B(1)
A->B(2)
A->B(3)
>>> # Aucun arc de D à A
>>> for a in g.getArcsBetween(D,A):
...     print(a.getNom())
...
Expand source code
def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
    """Renvoie tous les arcs entre noeudDepart et noeudArrivee.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs.
    - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
    >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
    >>> DE = Arc('D->E',D,E,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,D,E])
    >>> g.setArcs([AB1,AB2,AB3,DE,EA])
    >>> for a in g.getArcsBetween(A,B):
    ...     print(a.getNom())
    ...
    A->B(1)
    A->B(2)
    A->B(3)
    >>> # Aucun arc de D à A
    >>> for a in g.getArcsBetween(D,A):
    ...     print(a.getNom())
    ...

    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

    arcs = []
    for arc in self.getArcs():
        if (arc.getNoeudDebut() == noeudDepart and
                arc.getNoeudFin() == noeudArrivee):
            arcs.append(arc)
    return arcs
def getArcsCreanciers(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie une liste de tous les arcs partants de noeudDepart.

Paramètres

noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.

Tests

>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DE,EA])
>>> for a in g.getArcsCreanciers(A):
...     print(a.getNom())
...
A->B(1)
A->B(2)
A->B(3)
>>> for a in g.getArcsCreanciers(D):
...     print(a.getNom())
...
D->E
Expand source code
def getArcsCreanciers(self, noeudDepart: Noeud) -> list:
    """Renvoie une liste de tous les arcs partants de noeudDepart.

    Paramètres
    ----------
    noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
    >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
    >>> DE = Arc('D->E',D,E,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,D,E])
    >>> g.setArcs([AB1,AB2,AB3,DE,EA])
    >>> for a in g.getArcsCreanciers(A):
    ...     print(a.getNom())
    ...
    A->B(1)
    A->B(2)
    A->B(3)
    >>> for a in g.getArcsCreanciers(D):
    ...     print(a.getNom())
    ...
    D->E
    """  # noqa : E501
    pass
    assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.isNoeudInGraphe(noeudDepart):
        raise ValueError("noeudDepart doit être un noeud du graphe.")
    else:
        arcsCreanciers = []
        for arc in self.getArcs():
            if arc.getNoeudDebut() == noeudDepart:
                arcsCreanciers.append(arc)
    return arcsCreanciers
def getArcsCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe. [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ du cycle.

Remarque

S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A,B,C])
>>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> # Il y a 6 cycles partants de A dans ce graphe
>>> # On les récupère tous en fixant la graine
>>> random.seed(1)
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-5->B
B-5->C
C-1->A
>>> random.seed(2)
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-5->B
B-5->C
C-8->A
>>> random.seed(6)
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-2->B
B-5->C
C-1->A
>>> random.seed(4)
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...        
A-2->B
B-5->C
C-8->A
>>> random.seed(5)        
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...        
A-3->C
C-1->A
>>> random.seed(24)        
>>> for arc in g2.getArcsCycleFromNoeud(A):
...     print(arc.getNom())
...        
A-3->C
C-8->A
Expand source code
def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
    """Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe.
    [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ du cycle.

    Remarque
    --------
    S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g2 = Graphe('graphe2')
    >>> g2.setNoeuds([A,B,C])
    >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g2)
    Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> # Il y a 6 cycles partants de A dans ce graphe
    >>> # On les récupère tous en fixant la graine
    >>> random.seed(1)
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-5->B
    B-5->C
    C-1->A
    >>> random.seed(2)
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-5->B
    B-5->C
    C-8->A
    >>> random.seed(6)
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-2->B
    B-5->C
    C-1->A
    >>> random.seed(4)
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...        
    A-2->B
    B-5->C
    C-8->A
    >>> random.seed(5)        
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...        
    A-3->C
    C-1->A
    >>> random.seed(24)        
    >>> for arc in g2.getArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...        
    A-3->C
    C-8->A
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    arcsCycle = []
    if self.hasCycleFromNoeud(noeudDepart):
        noeudsPossibles = []
        for noeud in self.getCreanciers(noeudDepart):
            if (self.getNodesWays(noeud, noeudDepart) and
                    noeud not in noeudsPossibles):
                noeudsPossibles.append(noeud)
        # On choisit aléatoirement un premier noeud parmi ceux
        # qui permettent de revenir à noeudDepart
        noeudAleatoire = random.choice(noeudsPossibles)
        # On ajoute le début du chemin
        arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire))
        # On ajoute le reste du chemin
        arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart))

    return arcsCycle
def getArcsDirects(self) ‑> list

Renvoie la liste des arcs directs du graphe.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
>>> # Pour les tests, on ajoute les arcs inverses à la main
>>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
>>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0)
>>> arcsInverses = [BA5i,BA2i,CA3i]
>>> for arc in arcsInverses:
...     arc.setEstInverse(True)
>>> g.addArcs([BA5i,BA2i,CA3i])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
>>> for arc in g.getArcsDirects():
...     print(arc.getNom())
...
A-5->B
A-2->B
A-3->C
>>> g.addArcsInversesNuls()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
>>> for arc in g.getArcsDirects():
...     print(arc.getNom())
...
A-7->B
A-3->C
Expand source code
def getArcsDirects(self) -> list:
    """Renvoie la liste des arcs directs du graphe.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
    >>> # Pour les tests, on ajoute les arcs inverses à la main
    >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
    >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0)
    >>> arcsInverses = [BA5i,BA2i,CA3i]
    >>> for arc in arcsInverses:
    ...     arc.setEstInverse(True)
    >>> g.addArcs([BA5i,BA2i,CA3i])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
    >>> for arc in g.getArcsDirects():
    ...     print(arc.getNom())
    ...
    A-5->B
    A-2->B
    A-3->C
    >>> g.addArcsInversesNuls()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
    >>> for arc in g.getArcsDirects():
    ...     print(arc.getNom())
    ...
    A-7->B
    A-3->C
    """  # noqa : E501
    pass
    arcsDirects = []
    for arc in self.getArcs():
        if not arc.getEstInverse():
            arcsDirects.append(arc)
    return arcsDirects
def getArcsInverses(self) ‑> list

Renvoie la liste des arcs inverses du graphe.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
>>> # Pour les tests, on ajoute les arcs inverses à la main
>>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
>>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True)
>>> arcsInverses = [BA5i,BA2i,CA3i]
>>> g.addArcs([BA5i,BA2i,CA3i])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
>>> for arc in g.getArcsInverses():
...     print(arc.getNom())
B-5->A(i)
B-2->A(i)
C-3->A(i)
>>> g.addArcsInversesNuls()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
>>> for arc in g.getArcsInverses():
...     print(arc.getNom())
...
B-0->A
C-0->A
Expand source code
def getArcsInverses(self) -> list:
    """Renvoie la liste des arcs inverses du graphe.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
    >>> # Pour les tests, on ajoute les arcs inverses à la main
    >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
    >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True)
    >>> arcsInverses = [BA5i,BA2i,CA3i]
    >>> g.addArcs([BA5i,BA2i,CA3i])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
    >>> for arc in g.getArcsInverses():
    ...     print(arc.getNom())
    B-5->A(i)
    B-2->A(i)
    C-3->A(i)
    >>> g.addArcsInversesNuls()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
    >>> for arc in g.getArcsInverses():
    ...     print(arc.getNom())
    ...
    B-0->A
    C-0->A
    """  # noqa : E501
    pass
    arcsInverses = []
    for arc in self.getArcs():
        if arc.getEstInverse():
            arcsInverses.append(arc)
    return arcsInverses
def getArcsWay(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list

Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
  • noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

Remarque

Ne trouve pas les cycles Choisit un arc au hasard s'il existe plusieurs arcs parallèles.

noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> CB = Arc('C->B',C,B,1)
>>> CBbis = Arc('C-2->B',C,B,2)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA])        
>>> # Aucun chemin de B à C
>>> for arc in g.getArcsWay(B,C):
...     print(arc)
...
>>> # Dans ce graphe trois chemins sont possibles entre A et B
>>> # Deux des trois chemins présentent des arcs parallèles.
>>> # On fixe la graine pour les tests
>>> random.seed(13)        
>>> for arc in g.getArcsWay(A,B):
...     print(arc.getNom())
...
A->C
C->B
>>> random.seed(27)        
>>> for arc in g.getArcsWay(A,B):
...     print(arc.getNom())
...
A->C
C-2->B
>>> random.seed(3)
>>> for arc in g.getArcsWay(A,B):
...     print(arc.getNom())
...
A->B
Expand source code
def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
    """Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
    Renvoie [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
    - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

    Remarque
    --------
    Ne trouve pas les cycles
    Choisit un arc au hasard s'il existe plusieurs arcs parallèles.

    noeudDepart  : Noeud - noeud de départ
    noeudArrivee : Noeud - noeud d'arrivée

    Tests
    ----- 
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> CBbis = Arc('C-2->B',C,B,2)
    >>> DE = Arc('D->E',D,E,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA])        
    >>> # Aucun chemin de B à C
    >>> for arc in g.getArcsWay(B,C):
    ...     print(arc)
    ...
    >>> # Dans ce graphe trois chemins sont possibles entre A et B
    >>> # Deux des trois chemins présentent des arcs parallèles.
    >>> # On fixe la graine pour les tests
    >>> random.seed(13)        
    >>> for arc in g.getArcsWay(A,B):
    ...     print(arc.getNom())
    ...
    A->C
    C->B
    >>> random.seed(27)        
    >>> for arc in g.getArcsWay(A,B):
    ...     print(arc.getNom())
    ...
    A->C
    C-2->B
    >>> random.seed(3)
    >>> for arc in g.getArcsWay(A,B):
    ...     print(arc.getNom())
    ...
    A->B
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

    if self.getNodesWays(noeudDepart, noeudArrivee):
        arcsWay = random.choice(
            self.getArcsWays(noeudDepart, noeudArrivee)
        )
    else:
        arcsWay = []

    return arcsWay
def getArcsWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud, way=None) ‑> list

Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ du chemin.
  • noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
  • way : list - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.

Remarques

  • Ne trouve pas les cycles
  • S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.

Tests

>>> g = Graphe('grapheLinéaire')
>>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1)
>>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1)
>>> g.setNoeuds([A,B,C,D])
>>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4])        
>>> # Il y a 24 chemins d'arcs possibles entre A et D
>>> len(g.getArcsWays(A,D))
24
>>> # Il y a 6 chemins d'arcs possibles entre A et C
>>> # On les liste tous
>>> len(g.getArcsWays(A,C))
6
>>> for arcsWay in g.getArcsWays(A,C):
...     print("=== Chemin d'arcs entre A et C ===")
...     for arc in arcsWay:
...         print(arc.getNom())
...
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(2)
>>> # Tests sur un graphe non linéaire
>>> # On ajoute deux arcs entre C et A
>>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1)
>>> # On ajoute un arc entre A et C
>>> AC1 = Arc('A->C(1)',A,C,1)
>>> # Le graphe aura donc un cycle  ABCA
>>> # La méthode ne tolèrant pas de repasser par le noeud de départ
>>> # Elle trouve bien 7 chemins entre A et C
>>> g1 = Graphe('grapheNonLinéaire')
>>> g1.setNoeuds([A,B,C,D])
>>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1])        
>>> len(g1.getArcsWays(A,C))
7
>>> for arcsWay in g1.getArcsWays(A,C):
...     print("=== Chemin d'arcs entre A et C ===")
...     for arc in arcsWay:
...         print(arc.getNom())
...
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->C(1)
Expand source code
def getArcsWays(
    self,
    noeudDepart: Noeud,
    noeudArrivee: Noeud,
    way=None
) -> list:
    """Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee
    s'il en existe.
    Renvoie [] sinon.        

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ du chemin.
    - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
    - way          : list  - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.

    Remarques
    ---------        
    - Ne trouve pas les cycles 
    - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
    ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

    Tests
    ----- 
    >>> g = Graphe('grapheLinéaire')
    >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
    >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
    >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1)
    >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1)
    >>> g.setNoeuds([A,B,C,D])
    >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4])        
    >>> # Il y a 24 chemins d'arcs possibles entre A et D
    >>> len(g.getArcsWays(A,D))
    24
    >>> # Il y a 6 chemins d'arcs possibles entre A et C
    >>> # On les liste tous
    >>> len(g.getArcsWays(A,C))
    6
    >>> for arcsWay in g.getArcsWays(A,C):
    ...     print("=== Chemin d'arcs entre A et C ===")
    ...     for arc in arcsWay:
    ...         print(arc.getNom())
    ...
    === Chemin d'arcs entre A et C ===
    A->B(1)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(1)
    B->C(2)
    === Chemin d'arcs entre A et C ===
    A->B(2)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(2)
    B->C(2)
    === Chemin d'arcs entre A et C ===
    A->B(3)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(3)
    B->C(2)
    >>> # Tests sur un graphe non linéaire
    >>> # On ajoute deux arcs entre C et A
    >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1)
    >>> # On ajoute un arc entre A et C
    >>> AC1 = Arc('A->C(1)',A,C,1)
    >>> # Le graphe aura donc un cycle  ABCA
    >>> # La méthode ne tolèrant pas de repasser par le noeud de départ
    >>> # Elle trouve bien 7 chemins entre A et C
    >>> g1 = Graphe('grapheNonLinéaire')
    >>> g1.setNoeuds([A,B,C,D])
    >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1])        
    >>> len(g1.getArcsWays(A,C))
    7
    >>> for arcsWay in g1.getArcsWays(A,C):
    ...     print("=== Chemin d'arcs entre A et C ===")
    ...     for arc in arcsWay:
    ...         print(arc.getNom())
    ...
    === Chemin d'arcs entre A et C ===
    A->B(1)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(1)
    B->C(2)
    === Chemin d'arcs entre A et C ===
    A->B(2)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(2)
    B->C(2)
    === Chemin d'arcs entre A et C ===
    A->B(3)
    B->C(1)
    === Chemin d'arcs entre A et C ===
    A->B(3)
    B->C(2)
    === Chemin d'arcs entre A et C ===
    A->C(1)
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501
    assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts"  # noqa : E501

    # Pour stocker tous les chemins d'arcs possibles
    arcsWays = []
    # On liste tous les chemins de noeuds
    cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee)
    cheminsDeNoeudsSansDoublons = []
    for chemin in cheminsDeNoeudsPossibles:
        if chemin not in cheminsDeNoeudsSansDoublons:
            cheminsDeNoeudsSansDoublons.append(chemin)
    # Pour chaque chemin de cheminsDeNoeudsSansDoublon
    # On va récupérer tous les arcs entre deux noeuds successifs
    # dans arcsWay
    for chemin in cheminsDeNoeudsSansDoublons:
        # le nombre d'espace inter Noeuds
        nbInterNoeuds = len(chemin)-1
        # Pour stocker tous les arcs possibles entre deux noeuds du chemin
        arcsWay = []
        for i in range(nbInterNoeuds):
            arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1]))

        # On va stocker toutes les combinaisons d'arcs de ce chemin
        produitCartesienDesArcs = []
        for combs in product(*arcsWay):
            produitCartesienDesArcs.append(combs)
        arcsWays.extend(produitCartesienDesArcs)

    return arcsWays
def getCreanciers(self, noeudCandidat: models.noeud.Noeud) ‑> list

Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.

Tests

>>> g1 = Graphe('graphe1')
>>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5')
>>> g1.setNoeuds([n1,n2,n3,n4,n5])
>>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
>>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) 
>>> g1.setArcs([a1,a2,a3,a4,a5,a6])
>>> # On affiche uniquement les noms des noeuds créanciers pour le doctest
>>> for noeud in g1.getCreanciers(n1):
...     print(noeud.nom)
...
n4
n3
n5
>>> for noeud in g1.getCreanciers(n2):
...     print(noeud.nom)
...
n1
>>> for noeud in g1.getCreanciers(n5):
...     print(noeud.nom)
...
Expand source code
def getCreanciers(self, noeudCandidat: Noeud) -> list:
    """Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5')
    >>> g1.setNoeuds([n1,n2,n3,n4,n5])
    >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
    >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) 
    >>> g1.setArcs([a1,a2,a3,a4,a5,a6])
    >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest
    >>> for noeud in g1.getCreanciers(n1):
    ...     print(noeud.nom)
    ...
    n4
    n3
    n5
    >>> for noeud in g1.getCreanciers(n2):
    ...     print(noeud.nom)
    ...
    n1
    >>> for noeud in g1.getCreanciers(n5):
    ...     print(noeud.nom)
    ...
    
    """  # noqa : E501
    pass
    assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.isNoeudInGraphe(noeudCandidat):
        raise ValueError("noeudCandidat doit être un noeud du graphe.")
    else:
        noeudsCreanciers = []
        for arc in self.getArcs():
            if arc.getNoeudDebut() == noeudCandidat:
                noeudsCreanciers.append(arc.getNoeudFin())
    return noeudsCreanciers
def getDebiteurs(self, noeudCandidat: models.noeud.Noeud) ‑> list

Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.

Tests

>>> g1 = Graphe('graphe1')
>>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4')
>>> g1.setNoeuds([n1,n2,n3,n4])
>>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
>>> g1.setArcs([a1,a2,a3,a4])
>>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest
>>> for noeud in g1.getDebiteurs(n1):
...     print(noeud.nom)
...
n2
n3
n4
>>> for noeud in g1.getDebiteurs(n2):
...     print(noeud.nom)
...
>>> for noeud in g1.getDebiteurs(n4):
...     print(noeud.nom)
...
n1
Expand source code
def getDebiteurs(self, noeudCandidat: Noeud) -> list:
    """Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.
    
    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4')
    >>> g1.setNoeuds([n1,n2,n3,n4])
    >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
    >>> g1.setArcs([a1,a2,a3,a4])
    >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest
    >>> for noeud in g1.getDebiteurs(n1):
    ...     print(noeud.nom)
    ...
    n2
    n3
    n4
    >>> for noeud in g1.getDebiteurs(n2):
    ...     print(noeud.nom)
    ...
    
    >>> for noeud in g1.getDebiteurs(n4):
    ...     print(noeud.nom)
    ...
    n1
    """  # noqa : E501
    pass
    assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.isNoeudInGraphe(noeudCandidat):
        raise ValueError("noeudCandidat doit être un noeud du graphe.")
    else:
        noeudsDebiteurs = []
        for arc in self.getArcs():
            if arc.getNoeudFin() == noeudCandidat:
                noeudsDebiteurs.append(arc.getNoeudDebut())
    return noeudsDebiteurs
def getEntreprises(self) ‑> list

Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises de la situation problème étudiée.

Tests

>>> g1 = Graphe('graphe1')
>>> g1.getEntreprises()
[]
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> # On affiche uniquement les noms des noeuds pour le doctest
>>> for noeud in g1.getEntreprises():
...     print(noeud.nom)
...
n1
n2
n3
Expand source code
def getEntreprises(self) -> list:
    """Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises
    de la situation problème étudiée.
    
    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> g1.getEntreprises()
    []
    >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
    >>> g1.setNoeuds([n1,n2,n3])
    >>> # On affiche uniquement les noms des noeuds pour le doctest
    >>> for noeud in g1.getEntreprises():
    ...     print(noeud.nom)
    ...
    n1
    n2
    n3
    """  # noqa : E501
    pass
    return self.getNoeuds()
def getMinPoidsFromCycle(self, cycle: list) ‑> int

Renvoie le plus petits poids des arcs contenus dans cycle.

Paramètres

cycle : list - Une liste d'arcs du graphe.

Remarque

Ici on gère les éventuels cycles parallèles

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> random.seed(None)
>>> random.seed(9)
>>> cycle = g.getArcsCycleFromNoeud(A)        
>>> for e in cycle:
...     print(e.getNom())
...
A-3->C
C-1->A
>>> g.getMinPoidsFromCycle(cycle)
1
>>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud
>>> random.seed(15)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
...     print(e.getNom())
...
A-5->B
B-5->C
C-8->A
>>> g.getMinPoidsFromCycle(cycle)
5
>>> random.seed(13)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
...     print(e.getNom())
...
A-2->B
B-5->C
C-8->A
>>> g.getMinPoidsFromCycle(cycle)
2
>>> random.seed(17)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
...     print(e.getNom())
...
A-2->B
B-5->C
C-1->A
>>> g.getMinPoidsFromCycle(cycle)
1
>>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud.
>>> # Mais il peut rester plusieurs cycles partant de A !
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> cycle = g1.getArcsCycleFromNoeud(A)
>>> for e in cycle:
...     print(e.getNom())
...
A-7->B
B-5->C
C-9->A
>>> g1.getMinPoidsFromCycle(cycle)
5
Expand source code
def getMinPoidsFromCycle(self, cycle: list) -> int:
    """Renvoie le plus petits poids des arcs contenus dans cycle.

    Paramètres
    ----------
    cycle : list - Une liste d'arcs du graphe.

    Remarque
    --------
    Ici on gère les éventuels cycles parallèles

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> random.seed(None)
    >>> random.seed(9)
    >>> cycle = g.getArcsCycleFromNoeud(A)        
    >>> for e in cycle:
    ...     print(e.getNom())
    ...
    A-3->C
    C-1->A
    >>> g.getMinPoidsFromCycle(cycle)
    1
    >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud
    >>> random.seed(15)
    >>> cycle = g.getArcsCycleFromNoeud(A)
    >>> for e in cycle:
    ...     print(e.getNom())
    ...
    A-5->B
    B-5->C
    C-8->A
    >>> g.getMinPoidsFromCycle(cycle)
    5
    >>> random.seed(13)
    >>> cycle = g.getArcsCycleFromNoeud(A)
    >>> for e in cycle:
    ...     print(e.getNom())
    ...
    A-2->B
    B-5->C
    C-8->A
    >>> g.getMinPoidsFromCycle(cycle)
    2
    >>> random.seed(17)
    >>> cycle = g.getArcsCycleFromNoeud(A)
    >>> for e in cycle:
    ...     print(e.getNom())
    ...
    A-2->B
    B-5->C
    C-1->A
    >>> g.getMinPoidsFromCycle(cycle)
    1
    >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud.
    >>> # Mais il peut rester plusieurs cycles partant de A !
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C])
    >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> g1.simplification()
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> cycle = g1.getArcsCycleFromNoeud(A)
    >>> for e in cycle:
    ...     print(e.getNom())
    ...
    A-7->B
    B-5->C
    C-9->A
    >>> g1.getMinPoidsFromCycle(cycle)
    5
    """  # noqa : E501
    pass
    # les éléments de cycle doivent être des instances de la classe Arc
    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    # Un booléen pour tester si tout est OK
    allOk = True
    for arc in cycle:
        assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc"  # noqa : E501
        if not isinstance(arc, Arc):
            allOk = False
            raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}')  # noqa : E501
        assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément."
        if len(cycle) < 1:
            allOk = False
            raise ValueError('Le cycle doit avoir au moins un élément.')
    if allOk:
        poidsMin = cycle[0].getPoids()
        i = 1
        while i < len(cycle):
            if cycle[i].getPoids() <= poidsMin:
                poidsMin = cycle[i].getPoids()
            i += 1

    return poidsMin
def getNodesCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe. [] sinon.

Paramètres

noeudDepart : Noeud - Le noeud de départ du cycle.

Remarque

S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.

Tests

>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for e in g.getNodesCycleFromNoeud(A):
...     print(e.getNom())
...
A
D
E
A
>>> g.getNodesCycleFromNoeud(B)
[]
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> # Il y a deux cycles partants de A dans ce graphe
>>> random.seed(3)
>>> for e in g1.getNodesCycleFromNoeud(A):
...     print(e.getNom())
...
A
B
A
>>> random.seed(7)
>>> for e in g1.getNodesCycleFromNoeud(A):
...     print(e.getNom())
...
A
C
B
A
Expand source code
def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list:
    """Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe.
    [] sinon.

    Paramètres
    ----------
    noeudDepart  : Noeud - Le noeud de départ du cycle.

    Remarque
    --------
    S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.

    Tests
    -----
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE = Arc('D->E',D,E,2)
    >>> EA = Arc('E->A',E,A,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
    >>> for e in g.getNodesCycleFromNoeud(A):
    ...     print(e.getNom())
    ...
    A
    D
    E
    A
    >>> g.getNodesCycleFromNoeud(B)
    []
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C,D,E])
    >>> g1.setArcs([AB,AC,BA,CB,DE,EA])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
    >>> # Il y a deux cycles partants de A dans ce graphe
    >>> random.seed(3)
    >>> for e in g1.getNodesCycleFromNoeud(A):
    ...     print(e.getNom())
    ...
    A
    B
    A
    >>> random.seed(7)
    >>> for e in g1.getNodesCycleFromNoeud(A):
    ...     print(e.getNom())
    ...
    A
    C
    B
    A
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    cycle = []
    if self.hasCycleFromNoeud(noeudDepart):
        cycle.append(noeudDepart)
        noeudsPossibles = []
        for noeud in self.getCreanciers(noeudDepart):
            if self.getNodesWays(noeud, noeudDepart):
                noeudsPossibles.append(noeud)
        noeudAleatoire = random.choice(noeudsPossibles)
        cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart)
        cheminAleatoire = random.choice(cheminsPossibles)
        cycle.extend(cheminAleatoire)

    return cycle
def getNodesWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud, way=None) ‑> list

Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ du chemin.
  • noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
  • way : list - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee

Remarques

  • Fonction recursive
  • Ne trouve pas les cycles
  • S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])        
>>> # Aucun chemin de B à C
>>> for chemin in g.getNodesWays(B,C):
...     print("====chemin====")
...     for n in chemin:
...         print(n)
...
>>> # Deux chemins entre A et B
>>> for chemin in g.getNodesWays(A,B):
...     print("====chemin====")
...     for n in chemin:
...         print(n)
...
====chemin====
Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
====chemin====
Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
>>> g1 = Graphe('graphe1')
>>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1')
>>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1)
>>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1)
>>> C1A1 = Arc('C1->A1',C1,A1,1)
>>> C1D1 = Arc('C1->D1',C1,D1,1)
>>> g1.setNoeuds([A1,B1,C1,D1])
>>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1])
>>> # Six chemins entre A1 et D1
>>> for chemin in g1.getNodesWays(A1,D1):
...     print("====chemin====")
...     for n in chemin:
...         print(n)
...
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
Expand source code
def getNodesWays(
    self,
    noeudDepart: Noeud,
    noeudArrivee: Noeud,
    way=None
) -> list:
    """Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et
    noeudArrivee s'il en existe.
    Renvoie [] sinon.        

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ du chemin.
    - noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
    - way          : list  - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee

    Remarques
    ---------
    - Fonction recursive
    - Ne trouve pas les cycles 
    - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
    ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.       

    Tests
    ----- 
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE = Arc('D->E',D,E,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA])        
    >>> # Aucun chemin de B à C
    >>> for chemin in g.getNodesWays(B,C):
    ...     print("====chemin====")
    ...     for n in chemin:
    ...         print(n)
    ...
    >>> # Deux chemins entre A et B
    >>> for chemin in g.getNodesWays(A,B):
    ...     print("====chemin====")
    ...     for n in chemin:
    ...         print(n)
    ...
    ====chemin====
    Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
    ====chemin====
    Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
    >>> g1 = Graphe('graphe1')
    >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1')
    >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1)
    >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1)
    >>> C1A1 = Arc('C1->A1',C1,A1,1)
    >>> C1D1 = Arc('C1->D1',C1,D1,1)
    >>> g1.setNoeuds([A1,B1,C1,D1])
    >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1])
    >>> # Six chemins entre A1 et D1
    >>> for chemin in g1.getNodesWays(A1,D1):
    ...     print("====chemin====")
    ...     for n in chemin:
    ...         print(n)
    ...
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    ====chemin====
    Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
    Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
    Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
    Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(way, list) or way is None, "way vaut None ou est une liste."  # noqa : E501

    if way is None:
        way = []
    way = way + [noeudDepart]
    if noeudDepart == noeudArrivee:
        return [way]
    ways = []
    nonVisites = []
    # Pour l'algorithme de Klein, notamment,
    # le fait de rechercher tous les chemins
    # fait exploser le temps de traitement sur des graphes de
    # taille réelle dès Gen_100
    creanciersNoeudDepart = self.getCreanciers(noeudDepart)
    for noeud in creanciersNoeudDepart:
        if noeud not in way:
            nonVisites.append(noeud)

    for noeud in nonVisites:
        ways.extend(self.getNodesWays(noeud, noeudArrivee, way))
    return ways
def getNoeuds(self) ‑> list

Renvoie le tableau des noeuds du graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addNoeud(n3)
>>> for noeud in g1.getNoeuds():
...     print(noeud)
...
Noeud(nom=n1, voisins=[], nbArcs=0, capital=0)
Noeud(nom=n2, voisins=[], nbArcs=0, capital=0)
Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
Expand source code
def getNoeuds(self) -> list:
    """Renvoie le tableau des noeuds du graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1 = Noeud('n1')
    >>> n2 = Noeud('n2')
    >>> n3 = Noeud('n3')
    >>> g1.addNoeud(n1)
    >>> g1.addNoeud(n2)
    >>> g1.addNoeud(n3)
    >>> for noeud in g1.getNoeuds():
    ...     print(noeud)
    ...
    Noeud(nom=n1, voisins=[], nbArcs=0, capital=0)
    Noeud(nom=n2, voisins=[], nbArcs=0, capital=0)
    Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
    """
    pass
    return self.noeuds
def getNom(self) ‑> str

Renvoie le nom du graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> g1.getNom()
'graphe1'
Expand source code
def getNom(self) -> str:
    """Renvoie le nom du graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> g1.getNom()
    'graphe1'
    """
    pass
    return self.nom
def hasArc(self) ‑> bool

Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe est une liste vide ou non.

Tests

>>> g1 = Graphe('graphe1')
>>> n1,n2 = Noeud('n1'),Noeud('n2')
>>> arc = Arc('n1n2',n1,n2,1)
>>> g1.hasArc()
False
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addArc(arc)
>>> g1.hasArc()
True
Expand source code
def hasArc(self) -> bool:
    """Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe
    est une liste vide ou non.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1,n2 = Noeud('n1'),Noeud('n2')
    >>> arc = Arc('n1n2',n1,n2,1)
    >>> g1.hasArc()
    False
    >>> g1.addNoeud(n1)
    >>> g1.addNoeud(n2)
    >>> g1.addArc(arc)
    >>> g1.hasArc()
    True
    """  # noqa : E501
    pass

    hasArc = False
    if self.getArcs():
        hasArc = True

    return hasArc
def hasArcInverse(self, arcCandidat: models.arc.Arc) ‑> bool

Renvoie un booléen selon que le graphe contient un arc inverse de arcCandidat ou pas.

Paramètres

arcCandidat : Arc - L'arc dont on veut savoir si il a un arc inverse.

Remarques

  • L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein pour être un arc inverse introduit au debut de l'algorithme.
  • On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.hasArcInverse(AB)
False
>>> # On ajoute tous les arcs inverses au graphe
>>> g.addArcsInversesNuls()
>>> g.hasArcInverse(AB)
True
Expand source code
def hasArcInverse(self, arcCandidat: Arc) -> bool:
    """Renvoie un booléen selon que le graphe contient un arc inverse de
    arcCandidat ou pas.

    Paramètres
    ----------
    arcCandidat  : Arc - L'arc dont on veut savoir si il a un arc inverse.

    Remarques
    ---------
    - L'arc inverse n'est pas forcément un arc inverse dans le sens que
    l'arc d'un cycle de Klein pour être un arc inverse introduit au
    debut de l'algorithme.
    - On suppose que le graphe est simplifié au préalable afin qu'il n'y
    ait qu'un seul arc entre deux noeuds dans un sens donné.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
    >>> g.hasArcInverse(AB)
    False
    >>> # On ajoute tous les arcs inverses au graphe
    >>> g.addArcsInversesNuls()
    >>> g.hasArcInverse(AB)
    True
    """
    pass
    assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

    boolDeSortie = False
    if not arcCandidat.getEstInverse():
        for arc in self.getArcsInverses():
            if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                    arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                boolDeSortie = True
    if arcCandidat.getEstInverse():
        for arc in self.getArcsDirects():
            if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
                    arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
                boolDeSortie = True

    return boolDeSortie
def hasCycle(self) ‑> None

Renvoie un booléen selon que le graphe a encore au moins un cycle.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
>>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
>>> CB = Arc('2->1',C,B,1)
>>> DE = Arc('3->4',D,E,1)
>>> EA = Arc('4->0',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
>>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
>>> g.hasCycle()
True
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,AD,CB,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0'])
>>> # Ce graphe ne contient pas de cycle
>>> g1.hasCycle()
False
Expand source code
def hasCycle(self) -> None:
    """Renvoie un booléen selon que le graphe a encore au moins un cycle.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
    >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
    >>> CB = Arc('2->1',C,B,1)
    >>> DE = Arc('3->4',D,E,1)
    >>> EA = Arc('4->0',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
    >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
    >>> g.hasCycle()
    True
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C,D,E])
    >>> g1.setArcs([AB,AC,AD,CB,EA])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0'])
    >>> # Ce graphe ne contient pas de cycle
    >>> g1.hasCycle()
    False
    """  # noqa : E501
    pass
    boolDeSortie = False
    index = 0
    noeudsDuGraphe = self.getNoeuds()
    while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and
            index < len(noeudsDuGraphe)-1):
        index += 1
    if index < len(noeudsDuGraphe)-1:
        boolDeSortie = True
    return boolDeSortie
def hasCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> bool

Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.

Paramètres

noeudCandidat : Noeud - Un noeud du graphe.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
>>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
>>> CB = Arc('2->1',C,B,1)
>>> DE = Arc('3->4',D,E,1)
>>> EA = Arc('4->0',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
>>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
>>> print(g.hasCycleFromNoeud(A))
True
>>> print(g.hasCycleFromNoeud(B))
False
>>> print(g.hasCycleFromNoeud(C))
False
>>> print(g.hasCycleFromNoeud(D))
True
>>> print(g.hasCycleFromNoeud(E))
True
Expand source code
def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
    """Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.

    Paramètres
    ----------
    noeudCandidat : Noeud - Un noeud du graphe.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
    >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
    >>> CB = Arc('2->1',C,B,1)
    >>> DE = Arc('3->4',D,E,1)
    >>> EA = Arc('4->0',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
    >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
    >>> print(g.hasCycleFromNoeud(A))
    True
    >>> print(g.hasCycleFromNoeud(B))
    False
    >>> print(g.hasCycleFromNoeud(C))
    False
    >>> print(g.hasCycleFromNoeud(D))
    True
    >>> print(g.hasCycleFromNoeud(E))
    True
    """  # noqa : E501
    pass
    assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

    # Il suffit que l'un des creanciers du noeudCandidat
    # puisse accéder à nouveau à noeudCandidat
    boolDeSortie = False
    index = 1
    noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat)
    for index in range(1, len(noeudsAccessibles)):
        if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]):  # noqa : E501
            boolDeSortie = True
    return boolDeSortie
def hasNoeud(self) ‑> bool

Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe est une liste vide ou non.

Tests

>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> g1.hasNoeud()
False
>>> g1.addNoeud(n1)
>>> g1.hasNoeud()
True
Expand source code
def hasNoeud(self) -> bool:
    """Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe
    est une liste vide ou non.
    
    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1 = Noeud('n1')
    >>> g1.hasNoeud()
    False
    >>> g1.addNoeud(n1)
    >>> g1.hasNoeud()
    True
    """  # noqa : E501
    pass

    hasNoeud = False
    if self.getNoeuds():
        hasNoeud = True

    return hasNoeud
def isArcInGraphe(self, arcCandidat: models.arc.Arc) ‑> bool

Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.

Paramètres

arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2)
>>> g1.addArc(a1)
>>> g1.isArcInGraphe(a1)
True
>>> g1.isArcInGraphe(a2)
False
Expand source code
def isArcInGraphe(self, arcCandidat: Arc) -> bool:
    """Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.

    Paramètres
    ----------
    arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
    >>> g1.setNoeuds([n1,n2,n3])
    >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2)
    >>> g1.addArc(a1)
    >>> g1.isArcInGraphe(a1)
    True
    >>> g1.isArcInGraphe(a2)
    False
    """  # noqa : E501
    pass
    assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc."  # noqa : E501

    isArcInGraphe = False
    if arcCandidat in self.getArcs():
        isArcInGraphe = True
    return isArcInGraphe
def isNoeudInGraphe(self, noeudCandidat: models.noeud.Noeud) ‑> bool

Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> n1,n2 = Noeud('n1'),Noeud('n2')
>>> g1.addNoeud(n1)
>>> g1.isNoeudInGraphe(n1)
True
>>> g1.isNoeudInGraphe(n2)
False
Expand source code
def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool:
    """Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.
    
    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.
    
    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> n1,n2 = Noeud('n1'),Noeud('n2')
    >>> g1.addNoeud(n1)
    >>> g1.isNoeudInGraphe(n1)
    True
    >>> g1.isNoeudInGraphe(n2)
    False
    """  # noqa : E501
    pass
    assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

    isNoeudInGraphe = False
    if noeudCandidat in self.getNoeuds():
        isNoeudInGraphe = True
    return isNoeudInGraphe
def kleinAllArcsCycles(self) ‑> list

Renvoie tous les cycles d'un graphe sans doublons.

Remarques

  • Notamment deux cycles sont considérés comme identiques s'ils contiennent globalement les mêmes arcs.

Tests

>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
>>> # Mais 6 cycles distincts
>>> print(len(G3.kleinAllArcsCycles()))
6
>>> for cycle in G3.kleinAllArcsCycles():
...     print('=== cycle unique ===')
...     for arc in cycle:
...         print(arc.getNom())
...
=== cycle unique ===
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
=== cycle unique ===
A-4->B
B-2->E
E-7->F
F-5->A
=== cycle unique ===
A-2->E
E-7->F
F-5->A
=== cycle unique ===
B-6->C
C-5->D
D-2->B
=== cycle unique ===
B-2->E
E-7->F
F-3->C
C-5->D
D-2->B
=== cycle unique ===
C-5->D
D-4->E
E-7->F
F-3->C
Expand source code
def kleinAllArcsCycles(self) -> list:
    """Renvoie tous les cycles d'un graphe sans doublons.

    Remarques
    ---------
    - Notamment deux cycles sont considérés comme identiques s'ils
    contiennent globalement les mêmes arcs.

    Tests
    -----
    >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
    >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
    >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
    >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
    >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
    >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
    >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
    >>> G3 = Graphe('graphe3')
    >>> G3.setNoeuds([A, B, C, D, E, F])
    >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
    >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
    >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
    >>> # Mais 6 cycles distincts
    >>> print(len(G3.kleinAllArcsCycles()))
    6
    >>> for cycle in G3.kleinAllArcsCycles():
    ...     print('=== cycle unique ===')
    ...     for arc in cycle:
    ...         print(arc.getNom())
    ...
    === cycle unique ===
    A-4->B
    B-6->C
    C-5->D
    D-4->E
    E-7->F
    F-5->A
    === cycle unique ===
    A-4->B
    B-2->E
    E-7->F
    F-5->A
    === cycle unique ===
    A-2->E
    E-7->F
    F-5->A
    === cycle unique ===
    B-6->C
    C-5->D
    D-2->B
    === cycle unique ===
    B-2->E
    E-7->F
    F-3->C
    C-5->D
    D-2->B
    === cycle unique ===
    C-5->D
    D-4->E
    E-7->F
    F-3->C
    """
    pass

    # On récupère tous les noeuds
    noeuds = self.getNoeuds()
    # On récupère les cycles au fur et à mesure
    cyclesSansDoublons = []
    # On initialise avec tous les cycles du premier noeud
    for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]):
        cyclesSansDoublons.append(cycle)
    # Pour chaque noeuds
    for noeud in noeuds:
        # Pour chaque cycle partant du noeud
        for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
            toAdd = True
            for cycleUnique in cyclesSansDoublons:
                if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501
                    toAdd = False
            if toAdd:
                cyclesSansDoublons.append(cycle)

    return cyclesSansDoublons
def kleinAllArcsCyclesFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe

Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs des cycles.

Remarques

  • Le graphe constitué par les arcs directs doit être simplifié.
  • La méthode kleinAllArcsWays() exclut déjà les chemin passant deux fois par le même noeud.
  • On va ici encore exclure certains chemins cycliques à savoir :
    • ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
    • ceux qui n'ont que des arcs inverses
    • ceux qui ont un poids algébrique négatif.
  • Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g.estSimplifie()
True
>>> # Il y a 2 cycles partants de A dans ce graphe
>>> # Ce sont aussi des cycles de Klein                
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom())
...
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-3->C
C-9->A
>>> # On ajoute les arcs inverses manuellement pour les tests
>>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
>>> g.addArcs([BAi,CAi,CBi,ACi])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom())
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-7->B
B-5->C
C-1->A(i)
=== cycle ===
A-3->C
C-9->A
>>> # Tests sur un autre graphe
>>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G')
>>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4)
>>> # On ajoute un arc inverse manuellement
>>> GFi = Arc('G-3->F(i)',G,F,3,True)
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([D,E,F,G])
>>> g1.setArcs([DE,EF,FG,FD,GFi])
>>> # Ne présente qu'un cycle DEFD, 
>>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays()
>>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom())
...
=== cycle ===
D-1->E
E-2->F
F-4->D
>>> # Quelques arcs en plus qui pourraient poser problème
>>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6)
>>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True)
>>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi])
>>> # Il y a 2 cycles valides à partir de D
>>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom())
...
=== cycle ===
D-1->E
E-2->F
F-4->D
=== cycle ===
D-1->E
E-6->D
Expand source code
def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
    """Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe
    
    Renvoie [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs des cycles.
    
    Remarques
    ---------
    - Le graphe constitué par les arcs directs doit être simplifié.
    - La méthode kleinAllArcsWays() exclut déjà les chemin passant
    deux fois par le même noeud.
    - On va ici encore exclure certains chemins cycliques à savoir :
        - ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
        - ceux qui n'ont que des arcs inverses
        - ceux qui ont un poids algébrique négatif.
    - Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.

    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> g.estSimplifie()
    False
    >>> g.simplification()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> g.estSimplifie()
    True
    >>> # Il y a 2 cycles partants de A dans ce graphe
    >>> # Ce sont aussi des cycles de Klein                
    >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom())
    ...
    === cycle ===
    A-7->B
    B-5->C
    C-9->A
    === cycle ===
    A-3->C
    C-9->A
    >>> # On ajoute les arcs inverses manuellement pour les tests
    >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
    >>> g.addArcs([BAi,CAi,CBi,ACi])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
    >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom())
    === cycle ===
    A-7->B
    B-5->C
    C-9->A
    === cycle ===
    A-7->B
    B-5->C
    C-1->A(i)
    === cycle ===
    A-3->C
    C-9->A
    >>> # Tests sur un autre graphe
    >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G')
    >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4)
    >>> # On ajoute un arc inverse manuellement
    >>> GFi = Arc('G-3->F(i)',G,F,3,True)
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([D,E,F,G])
    >>> g1.setArcs([DE,EF,FG,FD,GFi])
    >>> # Ne présente qu'un cycle DEFD, 
    >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays()
    >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom())
    ...
    === cycle ===
    D-1->E
    E-2->F
    F-4->D
    >>> # Quelques arcs en plus qui pourraient poser problème
    >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6)
    >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True)
    >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi])
    >>> # Il y a 2 cycles valides à partir de D
    >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom())
    ...
    === cycle ===
    D-1->E
    E-2->F
    F-4->D
    === cycle ===
    D-1->E
    E-6->D
    """  # noqa : E501
    pass

    # On récupère tous les chemins cycliques
    allArcsKleinCycles = []
    if self.kleinHasCycleFromNoeud(noeudDepart):
        noeudsPossibles = []
        for noeud in self.getCreanciers(noeudDepart):
            if (self.kleinAllArcsWays(noeud, noeudDepart) and
                    noeud not in noeudsPossibles):
                noeudsPossibles.append(noeud)
        # Pour tous les noeuds qui permettent de revenir à noeudDepart
        for noeudPossible in noeudsPossibles:
            arcsKleinCycle = []
            # Pour tous les chemins entre un noeudPossible et noeudDepart
            suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501
            for chemin in suiteChemin:
                # pour tous les arcs qui permettent de joindre noeudDepart
                # et noeudPossible
                debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501
                for premierArc in debutChemin:
                    # On remet à zéro
                    arcsKleinCycle = []
                    # On ajoute le premier arc
                    arcsKleinCycle.extend([premierArc])
                    # On ajoute le reste du chemin
                    arcsKleinCycle.extend(chemin)
                    # On évite les doublons
                    if arcsKleinCycle not in allArcsKleinCycles:
                        allArcsKleinCycles.append(arcsKleinCycle)

        # Il faut encore nettoyer de certains chemins :
        #   -> ceux qui n'ont que deux arcs qui sont inverses
        #     l'un de l'autre
        #   -> ceux qui n'ont que des arcs inverses
        #   -> ceux qui ont un poids algébrique négatif.
        # ======== Remarque
        # On a besoin d'un variable globale pour éviter l'erreur
        # UnboundLocalError: local variable referenced before assignment python # noqa : E501
        # ========
        global allArcsKleinCyclesCleaned
        allArcsKleinCyclesCleaned = []
        for arcsKleinCycle in allArcsKleinCycles:
            cycleToKeep = True
            # ceux qui n'ont que deux arcs qui sont inverses
            # l'un de l'autre
            if len(arcsKleinCycle) == 2:
                arc0 = arcsKleinCycle[0]
                arc1 = arcsKleinCycle[1]
                if self.hasArcInverse(arc1):
                    if arc0 == self.getArcInverse(arc1):
                        cycleToKeep = False
                if self.hasArcInverse(arc0):
                    if arc1 == self.getArcInverse(arc0):
                        cycleToKeep = False
            # ceux qui n'ont que des arcs inverses
            onlyArcsInverses = True
            for arc in arcsKleinCycle:
                if not arc.getEstInverse():
                    onlyArcsInverses = False
            if onlyArcsInverses:
                cycleToKeep = False
            # ceux qui ont un poids algébrique négatif
            cycleWeight = 0
            for arc in arcsKleinCycle:
                if arc.getEstInverse():
                    cycleWeight -= arc.getPoids()
                if not arc.getEstInverse():
                    cycleWeight += arc.getPoids()
            if cycleWeight <= 0:
                cycleToKeep = False

            if cycleToKeep:
                allArcsKleinCyclesCleaned.append(arcsKleinCycle)

    return allArcsKleinCyclesCleaned
def kleinAllArcsWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list

Renvoie une liste de tuples :

  • d'arcs de poids non nul
  • et/ou d'arcs inverses de poids non nul
  • ne passant pas deux fois par le même noeud contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs des chemins.
  • noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.

Remarques

  • Ne trouve pas les cycles.
  • Méthode utile pour l'algorithme de Klein.
  • On ne peut pas supprimer de chemins ici en amont de la recherche de cycles

noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> ABi = Arc('A->B(i)',A,B,1,True)                
>>> BA1 = Arc('B->A(1)',B,A,0)
>>> BAi = Arc('B->A(i)',B,A,2,True)        
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
>>> g.estSimplifie()
True
>>> # Deux chemins de Klein de B à C
>>> # Mais on ne veut que les chemins :
>>> # --> qui ont des arcs directs de poids non nul
>>> # --> et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse qu'un
>>> for chemin in g.kleinAllArcsWays(B,C):
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
...
=== chemin de Klein ===
B->A(i)
A->C
>>> # Dans ce graphe cinq chemins sont possibles entre A et B
>>> # Mais on ne veut que les chemins :
>>> # --> qui ont des arcs directs de poids non nul
>>> # --> et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse que quatre
>>> for chemin in g.kleinAllArcsWays(A,B):
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins
>>> AB1.setPoids(1)
>>> for chemin in g.kleinAllArcsWays(A,B):
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
>>> # Le chemin doit être supprimé des possibles aussi
>>> # Cela laisse quatre chemins valides
>>> EB.setPoids(0)
>>> for chemin in g.kleinAllArcsWays(A,B):
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->B
Expand source code
def kleinAllArcsWays(
    self,
    noeudDepart: Noeud,
    noeudArrivee: Noeud
) -> list:
    """Renvoie une liste de tuples :

     - d'arcs de poids non nul         
     - et/ou d'arcs inverses de poids non nul         
     - ne passant pas deux fois par le même noeud
    contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
    Renvoie [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs des chemins.
    - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.

    Remarques
    ---------
    - Ne trouve pas les cycles.        
    - Méthode utile pour l'algorithme de Klein.
    - On ne peut pas supprimer de chemins ici en amont de la recherche de cycles

    noeudDepart  : Noeud - noeud de départ
    noeudArrivee : Noeud - noeud d'arrivée

    Tests
    ----- 
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> ABi = Arc('A->B(i)',A,B,1,True)                
    >>> BA1 = Arc('B->A(1)',B,A,0)
    >>> BAi = Arc('B->A(i)',B,A,2,True)        
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
    >>> g.estSimplifie()
    True
    >>> # Deux chemins de Klein de B à C
    >>> # Mais on ne veut que les chemins :
    >>> # --> qui ont des arcs directs de poids non nul
    >>> # --> et/ou des arcs inverses de poids non nul
    >>> # Cela n'en laisse qu'un
    >>> for chemin in g.kleinAllArcsWays(B,C):
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    ...
    === chemin de Klein ===
    B->A(i)
    A->C
    >>> # Dans ce graphe cinq chemins sont possibles entre A et B
    >>> # Mais on ne veut que les chemins :
    >>> # --> qui ont des arcs directs de poids non nul
    >>> # --> et/ou des arcs inverses de poids non nul
    >>> # Cela n'en laisse que quatre
    >>> for chemin in g.kleinAllArcsWays(A,B):
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->E
    E->B
    === chemin de Klein ===
    A->D
    D->B
    >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins
    >>> AB1.setPoids(1)
    >>> for chemin in g.kleinAllArcsWays(A,B):
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    === chemin de Klein ===
    A->B(1)
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->E
    E->B
    === chemin de Klein ===
    A->D
    D->B
    >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
    >>> # Le chemin doit être supprimé des possibles aussi
    >>> # Cela laisse quatre chemins valides
    >>> EB.setPoids(0)
    >>> for chemin in g.kleinAllArcsWays(A,B):
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    === chemin de Klein ===
    A->B(1)
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->B
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
    assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
    assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

    # Si il existe au moins un chemin entre noeudDepart et noeudArrivee
    # On ne garde que les chemins :
    #   -> dont les arcs ont des poids non nuls
    #   -> dont les arcs inverses ont des poids non nuls
    #   -> ne passant pas deux fois par le même noeud
    arcsWays = self.getArcsWays(noeudDepart, noeudArrivee)
    if arcsWays:
        # les chemins à garder, a priori vide
        arcsWaysOk = []
        for arcsWay in arcsWays:
            wayToKeep = True
            noeudsConnus = [noeudDepart]
            for arc in arcsWay:
                if (arc.getPoids() == 0 or
                        arc.getNoeudFin() in noeudsConnus):
                    wayToKeep = False
                if wayToKeep:
                    noeudsConnus.append(arc.getNoeudFin())
            noeudsConnus = []
            if wayToKeep:
                arcsWaysOk.append(arcsWay)
    else:
        arcsWaysOk = []

    return arcsWaysOk
def kleinArcsBetween(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list

Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs.
  • noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

Remarque

Méthode utile pour l'algorithme de Klein

Tests

>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DEg = Arc('D->E',D,E,1)
>>> EAg = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DEg,EAg])
>>> for a in g.kleinArcsBetween(A,B):
...     print(a.getNom())
...        
A->B(2)
A->B(3)
>>> # Aucun arc de D à A
>>> for a in g.kleinArcsBetween(D,A):
...     print(a.getNom())
...
>>> g1 = Graphe('graphe1')
>>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1')
>>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1)
>>> ABbis = Arc('A1->B1(2)',A1,B1,2)
>>> CB = Arc('C1->B1',C1,B1,1)
>>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1)
>>> EA = Arc('E1->A1',E1,A1,1)
>>> EB = Arc('E1->B1',E1,B1,1)
>>> g1.setNoeuds([A1,B1,C1,D1,E1])
>>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB])
>>> # print(g1.kleinArcsBetween(A1,B1))
>>> for a in g1.kleinArcsBetween(A1,B1):
...     print(a.getNom())
...
A1->B1
A1->B1(2)
Expand source code
def kleinArcsBetween(
    self,
    noeudDepart: Noeud,
    noeudArrivee: Noeud
) -> list:
    """Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs.
    - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.

    Remarque
    --------
    Méthode utile pour l'algorithme de Klein

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
    >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
    >>> DEg = Arc('D->E',D,E,1)
    >>> EAg = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,D,E])
    >>> g.setArcs([AB1,AB2,AB3,DEg,EAg])
    >>> for a in g.kleinArcsBetween(A,B):
    ...     print(a.getNom())
    ...        
    A->B(2)
    A->B(3)
    >>> # Aucun arc de D à A
    >>> for a in g.kleinArcsBetween(D,A):
    ...     print(a.getNom())
    ...
    >>> g1 = Graphe('graphe1')
    >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1')
    >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1)
    >>> ABbis = Arc('A1->B1(2)',A1,B1,2)
    >>> CB = Arc('C1->B1',C1,B1,1)
    >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1)
    >>> EA = Arc('E1->A1',E1,A1,1)
    >>> EB = Arc('E1->B1',E1,B1,1)
    >>> g1.setNoeuds([A1,B1,C1,D1,E1])
    >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB])
    >>> # print(g1.kleinArcsBetween(A1,B1))
    >>> for a in g1.kleinArcsBetween(A1,B1):
    ...     print(a.getNom())
    ...
    A1->B1
    A1->B1(2)
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501

    arcs = []
    for arc in self.getArcs():
        if (arc.getNoeudDebut() == noeudDepart and
                arc.getNoeudFin() == noeudArrivee and
                arc.getPoids() != 0):
            arcs.append(arc)
    return arcs
def kleinArcsCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list

Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.

Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs du cycle.

Remarques

  • S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
  • La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs parallèles entre deux noeuds soient fucionnés.
  • Un cycle ne doit pas passer plusieurs fois par le même noeud

Tests

>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g.estSimplifie()
True
>>> # Il y a 2 cycles partants de A dans ce graphe
>>> # Ce sont aussi des cycles de Klein
>>> # On les récupère tous en fixant la graine
>>> random.seed(1)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-7->B
B-5->C
C-9->A
>>> random.seed(5)        
>>> for arc in g.kleinArcsCycleFromNoeud(A):
...     print(arc.getNom())
...        
A-3->C
C-9->A
>>> # On ajoute les arcs inverses manuellement pour les tests
>>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
>>> g.addArcs([BAi,CAi,CBi,ACi])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
>>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom())
...
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-7->B
B-5->C
C-1->A(i)
=== cycle ===
A-3->C
C-9->A
>>> random.seed(1)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-7->B
B-5->C
C-9->A
>>> random.seed(5)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-3->C
C-9->A
>>> random.seed(7)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
...     print(arc.getNom())
...
A-7->B
B-5->C
C-1->A(i)
Expand source code
def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
    """Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.

    Renvoie [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs du cycle.

    Remarques
    ---------
    - S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
    - La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs
    parallèles entre deux noeuds soient fucionnés.
    - Un cycle ne doit pas passer plusieurs fois par le même noeud


    Tests
    -----
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
    >>> g.estSimplifie()
    False
    >>> g.simplification()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> g.estSimplifie()
    True
    >>> # Il y a 2 cycles partants de A dans ce graphe
    >>> # Ce sont aussi des cycles de Klein
    >>> # On les récupère tous en fixant la graine
    >>> random.seed(1)
    >>> for arc in g.kleinArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-7->B
    B-5->C
    C-9->A
    >>> random.seed(5)        
    >>> for arc in g.kleinArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...        
    A-3->C
    C-9->A
    >>> # On ajoute les arcs inverses manuellement pour les tests
    >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
    >>> g.addArcs([BAi,CAi,CBi,ACi])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
    >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles
    >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom())
    ...
    === cycle ===
    A-7->B
    B-5->C
    C-9->A
    === cycle ===
    A-7->B
    B-5->C
    C-1->A(i)
    === cycle ===
    A-3->C
    C-9->A
    >>> random.seed(1)
    >>> for arc in g.kleinArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-7->B
    B-5->C
    C-9->A
    >>> random.seed(5)
    >>> for arc in g.kleinArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-3->C
    C-9->A
    >>> random.seed(7)
    >>> for arc in g.kleinArcsCycleFromNoeud(A):
    ...     print(arc.getNom())
    ...
    A-7->B
    B-5->C
    C-1->A(i)
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501

    arcsKleinCycle = []
    if self.kleinHasCycleFromNoeud(noeudDepart):
        allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart)
        arcsKleinCycle = random.choice(allCycles)

    return arcsKleinCycle
def kleinArcsWay(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list

Renvoie une liste :

  • d'arcs de poids non nul
  • et/ou d'arcs inverses de poids non nul
  • ne passant pas deux fois par le même noeud

constituant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
  • noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

Remarque

Ne trouve pas les cycles.
Méthode utile pour l'algorithme de Klein.

noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> ABi = Arc('A->B(i)',A,B,1,True)                
>>> BA1 = Arc('B->A(1)',B,A,0)
>>> BAi = Arc('B->A(i)',B,A,2,True)        
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
>>> g.estSimplifie()
True
>>> # Un seul chemin de Klein de B à C
>>> for chemin in g.kleinAllArcsWays(B,C):        
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
...
=== chemin de Klein ===
B->A(i)
A->C
>>> # Dans ce graphe cinq chemins sont possibles entre A et B
>>> # Mais on ne veut que les chemins avec
>>> # des arcs directs de poids non nul        
>>> # et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse que quatre
>>> for chemin in g.kleinAllArcsWays(A,B):        
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
...
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On fixe la graine pour en avoir un au hasard
>>> random.seed(1)             
>>> for arc in g.kleinArcsWay(A,B):
...     print(arc.getNom())        
A->C
C->B
>>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins
>>> AB1.setPoids(1)        
>>> for chemin in g.kleinAllArcsWays(A,B):        
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
...
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On fixe la graine pour en avoir un au hasard
>>> random.seed(1)
>>> for arc in g.kleinArcsWay(A,B):
...     print(arc.getNom())
A->B(i)
>>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
>>> # Le chemin doit être supprimé des possibles aussi
>>> # Cela laisse quatre cemins valides        
>>> EB.setPoids(0)
>>> for chemin in g.kleinAllArcsWays(A,B):        
...     print("=== chemin de Klein ===")
...     for arc in chemin:
...         print(arc.getNom())
...
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->B
>>> # On a fixé la graine pour en avoir un au hasard
>>> for arc in g.kleinArcsWay(A,B):
...     print(arc.getNom())
A->B(1)
Expand source code
def kleinArcsWay(
    self,
    noeudDepart: Noeud,
    noeudArrivee: Noeud
) -> list:
    """Renvoie une liste : 
    
    - d'arcs de poids non nul
    - et/ou d'arcs inverses de poids non nul
    - ne passant pas deux fois par le même noeud

    constituant un chemin entre noeudDepart et noeudArrivee s'il en existe.
    Renvoie [] sinon.

    Paramètres
    ----------
    - noeudDepart  : Noeud - Le noeud de départ des arcs du chemin.
    - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.

    Remarque
    --------
    Ne trouve pas les cycles.        
    Méthode utile pour l'algorithme de Klein.

    noeudDepart  : Noeud - noeud de départ
    noeudArrivee : Noeud - noeud d'arrivée

    Tests
    ----- 
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> ABi = Arc('A->B(i)',A,B,1,True)                
    >>> BA1 = Arc('B->A(1)',B,A,0)
    >>> BAi = Arc('B->A(i)',B,A,2,True)        
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
    >>> g.estSimplifie()
    True
    >>> # Un seul chemin de Klein de B à C
    >>> for chemin in g.kleinAllArcsWays(B,C):        
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    ...
    === chemin de Klein ===
    B->A(i)
    A->C
    >>> # Dans ce graphe cinq chemins sont possibles entre A et B
    >>> # Mais on ne veut que les chemins avec
    >>> # des arcs directs de poids non nul        
    >>> # et/ou des arcs inverses de poids non nul
    >>> # Cela n'en laisse que quatre
    >>> for chemin in g.kleinAllArcsWays(A,B):        
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    ...
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->E
    E->B
    === chemin de Klein ===
    A->D
    D->B
    >>> # On fixe la graine pour en avoir un au hasard
    >>> random.seed(1)             
    >>> for arc in g.kleinArcsWay(A,B):
    ...     print(arc.getNom())        
    A->C
    C->B
    >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins
    >>> AB1.setPoids(1)        
    >>> for chemin in g.kleinAllArcsWays(A,B):        
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    ...
    === chemin de Klein ===
    A->B(1)
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->E
    E->B
    === chemin de Klein ===
    A->D
    D->B
    >>> # On fixe la graine pour en avoir un au hasard
    >>> random.seed(1)
    >>> for arc in g.kleinArcsWay(A,B):
    ...     print(arc.getNom())
    A->B(i)
    >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
    >>> # Le chemin doit être supprimé des possibles aussi
    >>> # Cela laisse quatre cemins valides        
    >>> EB.setPoids(0)
    >>> for chemin in g.kleinAllArcsWays(A,B):        
    ...     print("=== chemin de Klein ===")
    ...     for arc in chemin:
    ...         print(arc.getNom())
    ...
    === chemin de Klein ===
    A->B(1)
    === chemin de Klein ===
    A->B(i)
    === chemin de Klein ===
    A->C
    C->B
    === chemin de Klein ===
    A->D
    D->B
    >>> # On a fixé la graine pour en avoir un au hasard
    >>> for arc in g.kleinArcsWay(A,B):
    ...     print(arc.getNom())
    A->B(1)
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud."  # noqa : E501
    assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts."  # noqa : E501
    assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles."  # noqa : E501

    arcsWayOk = []
    kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee)
    if kleinArcsWays:
        arcsWayOk = random.choice(kleinArcsWays)

    return arcsWayOk
def kleinBestArcsCycle(self) ‑> list

Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.

Remarques

  • Il semble qu'en choisissant à chaque étape le cycle qui réduit au maximum la dette globale, la réduction finale ne soit pas la meilleure.
  • Cette fonction est donc incorrecte

Tests

>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
>>> # Mais 6 cycles distincts
>>> print(len(G3.kleinAllArcsCycles()))
6
>>> G3.addArcsInversesNuls()
>>> bestCycle1 = G3.kleinBestArcsCycle()
>>> for arc in bestCycle1:
...     print(arc.getNom())
...
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
>>> G3.kleinModifierCycle(bestCycle1)
>>> bestCycle2 = G3.kleinBestArcsCycle()
>>> for arc in bestCycle2:
...     print(arc.getNom())
...
B-2->E
E-3->F
F-3->C
C-4->B
Expand source code
def kleinBestArcsCycle(self) -> list:
    """Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.

    Remarques
    ---------
    - Il semble qu'en choisissant à chaque étape le cycle qui réduit
    au maximum la dette globale, la réduction finale ne soit pas la
    meilleure.
    - Cette fonction est donc incorrecte

    Tests
    -----
    >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
    >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
    >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
    >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
    >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
    >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
    >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
    >>> G3 = Graphe('graphe3')
    >>> G3.setNoeuds([A, B, C, D, E, F])
    >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
    >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
    >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
    >>> # Mais 6 cycles distincts
    >>> print(len(G3.kleinAllArcsCycles()))
    6
    >>> G3.addArcsInversesNuls()
    >>> bestCycle1 = G3.kleinBestArcsCycle()
    >>> for arc in bestCycle1:
    ...     print(arc.getNom())
    ...
    A-4->B
    B-6->C
    C-5->D
    D-4->E
    E-7->F
    F-5->A
    >>> G3.kleinModifierCycle(bestCycle1)
    >>> bestCycle2 = G3.kleinBestArcsCycle()
    >>> for arc in bestCycle2:
    ...     print(arc.getNom())
    ...
    B-2->E
    E-3->F
    F-3->C
    C-4->B
    """
    pass
    # On récupère tous les cycles sans doublons
    allCycles = self.kleinAllArcsCycles()
    bestCycle = []
    reductionDeLaDette = 0
    for cycle in allCycles:
        reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle)
        if reductionDuCycle > reductionDeLaDette:
            reductionDeLaDette = reductionDuCycle
            bestCycle = []
            bestCycle.extend(cycle)

    return bestCycle
def kleinDeleteArcsInverses(self) ‑> None

Supprime tous les arcs inverses temporaires.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse :  False
A-3->C => arc inverse :  False
B-5->C => arc inverse :  False
C-9->A => arc inverse :  False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-7->B => arc inverse :  False
A-3->C => arc inverse :  False
B-5->C => arc inverse :  False
C-9->A => arc inverse :  False
B-0->A => arc inverse :  True
C-0->A => arc inverse :  True
C-0->B => arc inverse :  True
A-0->C => arc inverse :  True
>>> random.seed(1)
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ABCA
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-2->B => arc inverse :  False
A-3->C => arc inverse :  False
B-0->C => arc inverse :  False
C-4->A => arc inverse :  False
B-5->A => arc inverse :  True
C-0->A => arc inverse :  True
C-5->B => arc inverse :  True
A-5->C => arc inverse :  True
>>> # On supprime les arcs de poids nul
>>> g.kleinDeleteDettesNulles()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-2->B => arc inverse :  False
A-3->C => arc inverse :  False
C-4->A => arc inverse :  False
B-5->A => arc inverse :  True
C-5->B => arc inverse :  True
A-5->C => arc inverse :  True
>>> # On supprime les arcs inverses temporaires
>>> g.kleinDeleteArcsInverses()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-2->B => arc inverse :  False
A-3->C => arc inverse :  False
C-4->A => arc inverse :  False
Expand source code
def kleinDeleteArcsInverses(self) -> None:
    """Supprime tous les arcs inverses temporaires.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5 = Arc('B-5->C',B,C,5)
    >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
    >>> # On simplifie le graphe
    >>> g.simplification()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
    ...
    A-7->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-9->A => arc inverse :  False
    >>> # On ajoute les arcs inverses
    >>> g.addArcsInversesNuls()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-7->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-9->A => arc inverse :  False
    B-0->A => arc inverse :  True
    C-0->A => arc inverse :  True
    C-0->B => arc inverse :  True
    A-0->C => arc inverse :  True
    >>> random.seed(1)
    >>> # Ce graphe présente deux cycles : ABCA et ACA
    >>> # On modifie ABCA
    >>> g.kleinModifierCycleFromNoeud(A)
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-2->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-0->C => arc inverse :  False
    C-4->A => arc inverse :  False
    B-5->A => arc inverse :  True
    C-0->A => arc inverse :  True
    C-5->B => arc inverse :  True
    A-5->C => arc inverse :  True
    >>> # On supprime les arcs de poids nul
    >>> g.kleinDeleteDettesNulles()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-2->B => arc inverse :  False
    A-3->C => arc inverse :  False
    C-4->A => arc inverse :  False
    B-5->A => arc inverse :  True
    C-5->B => arc inverse :  True
    A-5->C => arc inverse :  True
    >>> # On supprime les arcs inverses temporaires
    >>> g.kleinDeleteArcsInverses()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-2->B => arc inverse :  False
    A-3->C => arc inverse :  False
    C-4->A => arc inverse :  False
    """  # noqa : E501
    pass

    # On récupère d'abord tous les arcs à supprimer
    # Car la suppression de proche en proche peut laisser
    # un arc à l'écart à cause de la modification du graphe
    arcsToDelete = []
    for arc in self.getArcs():
        if arc.getEstInverse():
            arcsToDelete.append(arc)
    # On supprime alors tous les arcs à supprimer du graphe
    for arc in arcsToDelete:
        self.removeArc(arc)
def kleinDeleteDettesNulles(self) ‑> None

Supprime tous les arcs de poids nul.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse :  False
A-3->C => arc inverse :  False
B-5->C => arc inverse :  False
C-9->A => arc inverse :  False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> random.seed(1)
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ABCA
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-2->B => arc inverse :  False
A-3->C => arc inverse :  False
B-0->C => arc inverse :  False
C-4->A => arc inverse :  False
B-5->A => arc inverse :  True
C-0->A => arc inverse :  True
C-5->B => arc inverse :  True
A-5->C => arc inverse :  True
>>> # On supprime les arcs de poids nul
>>> g.kleinDeleteDettesNulles()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-2->B => arc inverse :  False
A-3->C => arc inverse :  False
C-4->A => arc inverse :  False
B-5->A => arc inverse :  True
C-5->B => arc inverse :  True
A-5->C => arc inverse :  True
Expand source code
def kleinDeleteDettesNulles(self) -> None:
    """Supprime tous les arcs de poids nul.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5 = Arc('B-5->C',B,C,5)
    >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
    >>> # On simplifie le graphe
    >>> g.simplification()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
    ...
    A-7->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-9->A => arc inverse :  False
    >>> # On ajoute les arcs inverses
    >>> g.addArcsInversesNuls()
    >>> random.seed(1)
    >>> # Ce graphe présente deux cycles : ABCA et ACA
    >>> # On modifie ABCA
    >>> g.kleinModifierCycleFromNoeud(A)
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-2->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-0->C => arc inverse :  False
    C-4->A => arc inverse :  False
    B-5->A => arc inverse :  True
    C-0->A => arc inverse :  True
    C-5->B => arc inverse :  True
    A-5->C => arc inverse :  True
    >>> # On supprime les arcs de poids nul
    >>> g.kleinDeleteDettesNulles()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-2->B => arc inverse :  False
    A-3->C => arc inverse :  False
    C-4->A => arc inverse :  False
    B-5->A => arc inverse :  True
    C-5->B => arc inverse :  True
    A-5->C => arc inverse :  True
    """  # noqa : E501
    pass

    # On récupère d'abord tous les arcs à supprimer
    # Car la suppression de proche en proche peut laisser
    # un arc à l'écart à cause de la modification du graphe
    arcsToDelete = []
    for arc in self.getArcs():
        if arc.getPoids() == 0:
            arcsToDelete.append(arc)
    # On supprime alors tous les arcs à supprimer
    for arc in arcsToDelete:
        self.removeArc(arc)
def kleinEstCycle(self, arcsCycle: list) ‑> bool

Renvoie un booléen selon que arcsCycle est un cycle de Klein du graphe ou non.

Remarque

On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut les doublons or tous les cycles doivent être testés par exemple ABCA et BCAB sont un doublon du même cycle mais doivent être testés tous les deux.

Paramètres

arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.

Tests

>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
>>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)]
>>> G3.kleinEstCycle(cycleOK)
True
>>> G3.kleinEstCycle(cycleKO)
False
Expand source code
def kleinEstCycle(self, arcsCycle: list) -> bool:
    """Renvoie un booléen selon que arcsCycle est un cycle de Klein du
    graphe ou non.

    Remarque
    --------
    On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut
    les doublons or tous les cycles doivent être testés par exemple
    ABCA et BCAB sont un doublon du même cycle mais doivent être testés
    tous les deux.

    Paramètres
    ----------
    arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.

    Tests
    -----
    >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
    >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
    >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
    >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
    >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
    >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
    >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
    >>> G3 = Graphe('graphe3')
    >>> G3.setNoeuds([A, B, C, D, E, F])
    >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
    >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
    >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
    >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)]
    >>> G3.kleinEstCycle(cycleOK)
    True
    >>> G3.kleinEstCycle(cycleKO)
    False
    """
    pass
    for arc in arcsCycle:
        assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

    # booléen de sortie
    boolDeSortie = True
    # On récupère tous les cycles du graphe
    cyclesDuGraphe = []
    for noeud in self.getNoeuds():
        for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
            cyclesDuGraphe.append(cycle)
    if arcsCycle not in cyclesDuGraphe:
        boolDeSortie = False

    return boolDeSortie
def kleinHasCycle(self) ‑> None

Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.

Remarque

Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.kleinHasCycle()
True
>>> AB.setPoids(0)
>>> g.kleinHasCycle()
True
>>> BA.setPoids(0)
>>> g.kleinHasCycle()
True
>>> DE.setPoids(0)
>>> g.kleinHasCycle()
False
Expand source code
def kleinHasCycle(self) -> None:
    """Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.

    Remarque
    --------
    Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
    >>> g.kleinHasCycle()
    True
    >>> AB.setPoids(0)
    >>> g.kleinHasCycle()
    True
    >>> BA.setPoids(0)
    >>> g.kleinHasCycle()
    True
    >>> DE.setPoids(0)
    >>> g.kleinHasCycle()
    False
    """  # noqa : E501
    pass
    boolDeSortie = False
    index = 0
    noeudsDuGraphe = self.getNoeuds()
    while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and
            index < len(noeudsDuGraphe)-1):
        index += 1
    if index < len(noeudsDuGraphe)-1:
        boolDeSortie = True
    return boolDeSortie
def kleinHasCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> bool

Renvoie un booléen selon que le graphe présente un cycle de Klein ou non à partir du noeudCandidat.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud.

Remarque

Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

Tests

>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.kleinHasCycleFromNoeud(A)
True
>>> AB.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
True
>>> BA.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
True
>>> DE.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
False
Expand source code
def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
    """Renvoie un booléen selon que le graphe présente un cycle de Klein
    ou non à partir du noeudCandidat.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud.

    Remarque
    --------
    Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
    >>> EA = Arc('E->A',E,A,1)
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
    >>> g.kleinHasCycleFromNoeud(A)
    True
    >>> AB.setPoids(0)
    >>> g.kleinHasCycleFromNoeud(A)
    True
    >>> BA.setPoids(0)
    >>> g.kleinHasCycleFromNoeud(A)
    True
    >>> DE.setPoids(0)
    >>> g.kleinHasCycleFromNoeud(A)
    False
    """
    pass
    assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501

    # On récupère tous les noeuds situés à un arc de noeudCancidat
    noeudDirectementAccessibles = self.getCreanciers(noeudCandidat)
    boolDeSortie = False
    for noeud in noeudDirectementAccessibles:
        if self.kleinArcsBetween(noeudCandidat, noeud):
            if self.kleinAllArcsWays(noeud, noeudCandidat):
                boolDeSortie = True
    return boolDeSortie
def kleinModifierCycle(self, arcsCycle: list) ‑> None

Renvoie le graphe modifié après réduction du cycle.

Remarque

  • Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle.
  • Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle.

Paramètres

arcsCycle : list - Le cycle de Klein à réduire.

Tests

>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> G3.addArcsInversesNuls()
>>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
>>> for arc in cycleOK:
...     print(arc.getNom())
...
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
>>> G3.kleinModifierCycle(cycleOK)
>>> for arc in cycleOK:
...     print(arc.getNom())
...
A-0->B
B-2->C
C-1->D
D-0->E
E-3->F
F-1->A
Expand source code
def kleinModifierCycle(self, arcsCycle: list) -> None:
    """Renvoie le graphe modifié après réduction du cycle.

    Remarque
    --------
    - Chaque arc dans le sens du cycle est modifié en diminuant son poids
    du plus petit poids des arcs du cycle.
    - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
    son poids du plus petit poids des arcs du cycle.

    Paramètres
    ----------
    arcsCycle : list - Le cycle de Klein à réduire.

    Tests
    -----
    >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
    >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
    >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
    >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
    >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
    >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
    >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
    >>> G3 = Graphe('graphe3')
    >>> G3.setNoeuds([A, B, C, D, E, F])
    >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
    >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
    >>> G3.addArcsInversesNuls()
    >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
    >>> for arc in cycleOK:
    ...     print(arc.getNom())
    ...
    A-4->B
    B-6->C
    C-5->D
    D-4->E
    E-7->F
    F-5->A
    >>> G3.kleinModifierCycle(cycleOK)
    >>> for arc in cycleOK:
    ...     print(arc.getNom())
    ...
    A-0->B
    B-2->C
    C-1->D
    D-0->E
    E-3->F
    F-1->A
    """ # noqa : E501
    pass
    assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501
    for arc in arcsCycle:
        assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501

    minPoids = self.getMinPoidsFromCycle(arcsCycle)
    for arc in arcsCycle:
        # On diminue le poids de l'arc
        arc.setPoids(arc.getPoids()-minPoids)
        # On augmente le poids de son arc inverse
        arcInverse = self.getArcInverse(arc)
        arcInverse.setPoids(arcInverse.getPoids()+minPoids)
        # On renomme les arcs
        nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom()  # noqa : E501
        arc.setNom(nouveauNom)
        nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom()  # noqa : E501
        arcInverse.setNom(nouveauNomInverse)
def kleinModifierCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> None

Modifie un cycle de Klein pour un noeud du graphe s'il en existe, sinon le graphe n'est pas modifié.

  • Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle.
  • Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle.

Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un cycle de Klein.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud.

Remarque

  • Un cycle de Klein est un cycle :
    • dont tous les arcs ont un poids non nul.
    • dont le poids algébrique est strictement positif.
  • Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-7->B => arc inverse :  False
A-3->C => arc inverse :  False
B-5->C => arc inverse :  False
C-9->A => arc inverse :  False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-7->B => arc inverse :  False
A-3->C => arc inverse :  False
B-5->C => arc inverse :  False
C-9->A => arc inverse :  False
B-0->A => arc inverse :  True
C-0->A => arc inverse :  True
C-0->B => arc inverse :  True
A-0->C => arc inverse :  True
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ACA
>>> random.seed(11)
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
...
A-7->B => arc inverse :  False
A-0->C => arc inverse :  False
B-5->C => arc inverse :  False
C-6->A => arc inverse :  False
B-0->A => arc inverse :  True
C-3->A => arc inverse :  True
C-0->B => arc inverse :  True
A-3->C => arc inverse :  True
>>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
...     print("=== cycle ===")
...     for arc in cycle:
...         print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
=== cycle ===
A-7->B => arc inverse :  False
B-5->C => arc inverse :  False
C-6->A => arc inverse :  False
=== cycle ===
A-7->B => arc inverse :  False
B-5->C => arc inverse :  False
C-3->A => arc inverse :  True
Expand source code
def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None:
    """Modifie un cycle de Klein pour un noeud du graphe s'il en existe,
    sinon le graphe n'est pas modifié.

    - Chaque arc dans le sens du cycle est modifié en diminuant son poids
    du plus petit poids des arcs du cycle.
    - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
    son poids du plus petit poids des arcs du cycle.

    Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un
    cycle de Klein.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud.

    Remarque
    --------
    - Un cycle de Klein est un cycle :
        - dont tous les arcs ont un poids non nul.        
        - dont le poids algébrique est strictement positif.
    - Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> BC5 = Arc('B-5->C',B,C,5)
    >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
    >>> # On simplifie le graphe
    >>> g.simplification()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-7->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-9->A => arc inverse :  False
    >>> # On ajoute les arcs inverses
    >>> g.addArcsInversesNuls()
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-7->B => arc inverse :  False
    A-3->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-9->A => arc inverse :  False
    B-0->A => arc inverse :  True
    C-0->A => arc inverse :  True
    C-0->B => arc inverse :  True
    A-0->C => arc inverse :  True
    >>> # Ce graphe présente deux cycles : ABCA et ACA
    >>> # On modifie ACA
    >>> random.seed(11)
    >>> g.kleinModifierCycleFromNoeud(A)
    >>> for arc in g.getArcs():
    ...     print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())        
    ...
    A-7->B => arc inverse :  False
    A-0->C => arc inverse :  False
    B-5->C => arc inverse :  False
    C-6->A => arc inverse :  False
    B-0->A => arc inverse :  True
    C-3->A => arc inverse :  True
    C-0->B => arc inverse :  True
    A-3->C => arc inverse :  True
    >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse
    >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
    ...     print("=== cycle ===")
    ...     for arc in cycle:
    ...         print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
    ...
    === cycle ===
    A-7->B => arc inverse :  False
    B-5->C => arc inverse :  False
    C-6->A => arc inverse :  False
    === cycle ===
    A-7->B => arc inverse :  False
    B-5->C => arc inverse :  False
    C-3->A => arc inverse :  True
    """  # noqa : E501
    pass
    assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud."  # noqa : E501
    # Pour chaque arc du cycle, il doit y avoir un arc inverse
    for arc in self.getArcs():
        assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse"  # noqa : E501

    if self.kleinHasCycleFromNoeud(noeudCandidat):
        # On choisit un cycle à réduire au hasard
        cycleToChange = random.choice(
            self.kleinAllArcsCyclesFromNoeud(noeudCandidat)
        )
        self.kleinModifierCycle(cycleToChange)
def parcoursEnProfondeurArcs(self, noeudDepart: models.noeud.Noeud, dejaVisites=None) ‑> list

Renvoie les arcs du graphe accessibles depuis noeudDepart.

Paramètres

  • noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
  • dejaVisites : list - Les arcs déjà visités.

Remarque

  • Fonction recursive

Tests

>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for n in g.parcoursEnProfondeurArcs(A,[]):
...     print(n.getNom())
...
A->B
C->B
D->E
E->A
A->C
A->D
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for n in g1.parcoursEnProfondeurArcs(A,[]):
...     print(n.getNom())
...
A->B
B->A
A->C
C->B
Expand source code
def parcoursEnProfondeurArcs(
    self,
    noeudDepart: Noeud,
    dejaVisites=None
) -> list:
    """Renvoie les arcs du graphe accessibles depuis noeudDepart.

    Paramètres
    ----------
    - noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
    - dejaVisites : list  - Les arcs déjà visités.

    Remarque
    --------
    - Fonction recursive

    Tests
    -----
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE = Arc('D->E',D,E,2)
    >>> EA = Arc('E->A',E,A,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
    >>> for n in g.parcoursEnProfondeurArcs(A,[]):
    ...     print(n.getNom())
    ...
    A->B
    C->B
    D->E
    E->A
    A->C
    A->D
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C,D,E])
    >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
    >>> for n in g1.parcoursEnProfondeurArcs(A,[]):
    ...     print(n.getNom())
    ...
    A->B
    B->A
    A->C
    C->B
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc."  # noqa : E501
    assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

    if dejaVisites is None:
        dejaVisites = []
    for arc in self.getArcsCreanciers(noeudDepart):
        if arc not in dejaVisites:
            dejaVisites.append(arc)
            for voisin in self.getCreanciers(noeudDepart):
                self.parcoursEnProfondeurArcs(voisin, dejaVisites)

    return dejaVisites
def parcoursEnProfondeurNoeuds(self, noeudDepart: models.noeud.Noeud, dejaVisites=None) ‑> list

Renvoie les noeuds du graphe accessibles depuis noeudDepart.

Paramètres

  • noeudDepart : Noeud - Le noeud de départ de l'exploration.
  • dejaVisites : list - Une liste avec les noeuds déjà visités.

Remarque

  • Fonction recursive

Tests

>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for n in g.parcoursEnProfondeurNoeuds(A,[]):
...     print(n.getNom())
...
A
B
C
D
E
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for n in g1.parcoursEnProfondeurNoeuds(A,[]):
...     print(n.getNom())
...
A
B
C
Expand source code
def parcoursEnProfondeurNoeuds(
    self,
    noeudDepart: Noeud,
    dejaVisites=None
) -> list:
    """Renvoie les noeuds du graphe accessibles depuis noeudDepart.

    Paramètres
    ----------
    - noeudDepart : Noeud - Le noeud de départ de l'exploration.
    - dejaVisites : list  - Une liste avec les noeuds déjà visités.

    Remarque
    --------
    - Fonction recursive

    Tests
    -----
    >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
    >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
    >>> BA = Arc('B->A',B,A,1)
    >>> CB = Arc('C->B',C,B,1)
    >>> DE = Arc('D->E',D,E,2)
    >>> EA = Arc('E->A',E,A,3)
    >>> g = Graphe('graphe')
    >>> g.setNoeuds([A,B,C,D,E])
    >>> g.setArcs([AB,AC,AD,CB,DE,EA]) 
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
    >>> for n in g.parcoursEnProfondeurNoeuds(A,[]):
    ...     print(n.getNom())
    ...
    A
    B
    C
    D
    E
    >>> g1 = Graphe('graphe1')
    >>> g1.setNoeuds([A,B,C,D,E])
    >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) 
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
    >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]):
    ...     print(n.getNom())
    ...
    A
    B
    C
    """  # noqa : E501
    pass
    assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud."  # noqa : E501
    assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste."  # noqa : E501

    if dejaVisites is None:
        dejaVisites = []
    if noeudDepart not in dejaVisites:
        dejaVisites.append(noeudDepart)
        for voisin in self.getCreanciers(noeudDepart):
            self.parcoursEnProfondeurNoeuds(voisin, dejaVisites)

    return dejaVisites
def positionNette(self, noeudCandidat: models.noeud.Noeud, algo1NomEntreprise=None, algo1Don=0) ‑> int

Retourne la balance entre dettes et créances d'une instance de la classe Noeud.

Paramètres

  • noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
  • algo1NomEntreprise : str - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
  • algo1Don : int - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique

Tests

>>> g1 = Graphe('graphe1')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> g1.positionNette(A)
-1
>>> g1.positionNette(A,'A',5)
-6
>>> g1.positionNette(B)
2
>>> g1.positionNette(B,'A',5)
2
>>> g1.positionNette(C)
-1
Expand source code
def positionNette(
    self,
    noeudCandidat: Noeud,
    algo1NomEntreprise=None,
    algo1Don=0
) -> int:
    """Retourne la balance entre dettes et créances d'une instance de la classe Noeud.

    Paramètres
    ----------
    - noeudCandidat      : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
    - algo1NomEntreprise : str   - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
    - algo1Don           : int   - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g1.setNoeuds([A,B,C])
    >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> g1.positionNette(A)
    -1
    >>> g1.positionNette(A,'A',5)
    -6
    >>> g1.positionNette(B)
    2
    >>> g1.positionNette(B,'A',5)
    2
    >>> g1.positionNette(C)
    -1
    """  # noqa : E501
    pass
    assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.isNoeudInGraphe(noeudCandidat):
        raise ValueError("noeudCandidat doit être un noeud du graphe.")
    else:
        # Total des sommes dues aux créanciers du noeudCandidat
        detteTotaleDuNoeud = 0
        # Total des sommes dues par les débiteurs du noeudCandidat
        creanceTotaleDuNoeud = 0
        debiteurs = self.getDebiteurs(noeudCandidat)
        creanciers = self.getCreanciers(noeudCandidat)
        for arc in self.getArcs():
            noeudDebut = arc.getNoeudDebut()
            noeudFin = arc.getNoeudFin()
            if noeudDebut == noeudCandidat and noeudFin in creanciers:
                detteTotaleDuNoeud += arc.poids
            if noeudDebut in debiteurs and noeudFin == noeudCandidat:
                creanceTotaleDuNoeud += arc.poids
        if noeudCandidat.getNom() == algo1NomEntreprise:
            positionNetteDuNoeud = (noeudCandidat.getCapital()
                                    + creanceTotaleDuNoeud
                                    - detteTotaleDuNoeud
                                    - algo1Don)
        else:
            positionNetteDuNoeud = (noeudCandidat.getCapital()
                                    + creanceTotaleDuNoeud
                                    - detteTotaleDuNoeud)

    return positionNetteDuNoeud
def removeArc(self, arcCandidat: models.arc.Arc) ‑> None

Supprime une instance de la classe Arc du graphe.

Paramètres

arcCandidat : Arc - Un arc à supprimer du graphe.

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
>>> a1 = Arc('n1n2_1', n1, n2, 1)
>>> a2 = Arc('n1n2_2', n1, n2, 2)
>>> a3 = Arc('n1n3', n1, n3, 3)
>>> g1.setArcs([a1,a2,a3])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3'])
>>> g1.removeArc(a1)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3'])
>>> for n in g1.getNoeuds():
...     print(n)
...
Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0)
Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0)
>>> g1.removeArc(a3)
>>> for n in g1.getNoeuds():
...     print(n)
...
Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0)
Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
Expand source code
def removeArc(self, arcCandidat: Arc) -> None:
    """Supprime une instance de la classe Arc du graphe.

    Paramètres
    ----------
    arcCandidat : Arc - Un arc à supprimer du graphe.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=[], arcs=[])
    >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
    >>> g1.setNoeuds([n1,n2,n3])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
    >>> a1 = Arc('n1n2_1', n1, n2, 1)
    >>> a2 = Arc('n1n2_2', n1, n2, 2)
    >>> a3 = Arc('n1n3', n1, n3, 3)
    >>> g1.setArcs([a1,a2,a3])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3'])
    >>> g1.removeArc(a1)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3'])
    >>> for n in g1.getNoeuds():
    ...     print(n)
    ...
    Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0)
    Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
    Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0)
    >>> g1.removeArc(a3)
    >>> for n in g1.getNoeuds():
    ...     print(n)
    ...
    Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0)
    Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
    Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)

    """  # noqa : E501
    pass
    assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc."  # noqa : E501
    assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe."  # noqa : E501

    # Un booléen pour tester si tout est OK
    allOk = True
    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.isArcInGraphe(arcCandidat):
        allOk = False
        raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.")  # noqa : E501
    if allOk:
        self.getArcs().remove(arcCandidat)
        # Après suppression de l'arcCandidat,
        # Si il n'y a plus d'arcs entre eux
        # On met à jour le tableau des voisins des Noeuds
        # Sinon, on gère uniquement les nombres d'arcs des Noeuds !
        noeudDebut = arcCandidat.getNoeudDebut()
        noeudFin = arcCandidat.getNoeudFin()
        if ((noeudDebut not in self.getDebiteurs(noeudFin)) and
                (noeudDebut not in self.getCreanciers(noeudFin))):
            noeudDebut.removeVoisin(noeudFin)
        else:
            noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1)
            noeudFin.setNbArcs(noeudFin.getNbArcs() - 1)
def setArcs(self, nouveauxArcs: list) ‑> None

Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.

Paramètres

nouveauxArcs : list - Une liste d'instances de la classe Arcs.

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> nouveauxNoeuds = [n1, n2, n3]
>>> g1.setNoeuds(nouveauxNoeuds)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
>>> a1 = Arc('n1n2_0', n1, n2, 1)
>>> a2 = Arc('n1n2_1', n1, n2, 2)
>>> a3 = Arc('n1n3_0', n1, n3, 3)
>>> a4 = Arc('n2n3_0', n2, n3, 4)
>>> nouveauxArcs = [a1, a2, a3, a4]
>>> g1.setArcs(nouveauxArcs)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
Expand source code
def setArcs(self, nouveauxArcs: list) -> None:
    """Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.

    Paramètres
    ----------
    nouveauxArcs : list - Une liste d'instances de la classe Arcs.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=[], arcs=[])
    >>> n1 = Noeud('n1')
    >>> n2 = Noeud('n2')
    >>> n3 = Noeud('n3')
    >>> nouveauxNoeuds = [n1, n2, n3]
    >>> g1.setNoeuds(nouveauxNoeuds)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
    >>> a1 = Arc('n1n2_0', n1, n2, 1)
    >>> a2 = Arc('n1n2_1', n1, n2, 2)
    >>> a3 = Arc('n1n3_0', n1, n3, 3)
    >>> a4 = Arc('n2n3_0', n2, n3, 4)
    >>> nouveauxArcs = [a1, a2, a3, a4]
    >>> g1.setArcs(nouveauxArcs)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
    """  # noqa : E501
    pass
    # Un booléen pour tester si tout est OK
    allOk = True
    for nouvelArc in nouveauxArcs:
        assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent."  # noqa : E501
        if nouvelArc in self.getArcs():
            allOk = False
            raise ValueError("On ne peut ajouter un arc déjà présent.")
        assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe."  # noqa : E501
        if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or
                nouvelArc.getNoeudFin() not in self.getNoeuds()):
            allOk = False
            raise ValueError("NouvelArc doit lier deux noeuds du graphe.")
    if allOk:
        for arc in nouveauxArcs:
            self.addArc(arc)
def setNoeuds(self, nouveauxNoeuds: list) ‑> None

Définit le tableau des noeuds d'une instance de la classe Graphe.

Paramètres

nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.

Remarque:

On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.

Tests

>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> nouveauxNoeuds = [n1, n2, n3]
>>> g1.setNoeuds(nouveauxNoeuds)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
Expand source code
def setNoeuds(self, nouveauxNoeuds: list) -> None:
    """Définit le tableau des noeuds d'une instance de la classe Graphe.

    Paramètres
    ----------
    nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.

    Remarque:
    ---------
    On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=[], arcs=[])
    >>> n1 = Noeud('n1')
    >>> n2 = Noeud('n2')
    >>> n3 = Noeud('n3')
    >>> nouveauxNoeuds = [n1, n2, n3]
    >>> g1.setNoeuds(nouveauxNoeuds)
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
    """
    pass
    # Un booléen pour tester si tout est OK
    allOk = True
    for nouveauNoeud in nouveauxNoeuds:
        assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent."  # noqa : E501
        if nouveauNoeud in self.noeuds:
            allOk = False
            raise ValueError("On ne peut ajouter un noeud déjà présent.")
    if allOk:
        for noeud in nouveauxNoeuds:
            self.addNoeud(noeud)
def setNom(self, nouveauNom) ‑> str

Redéfinit le nom du graphe.

Paramètres

nouveauNom : str - Le nouveau nom du graphe

Tests

>>> g1 = Graphe('graphe1')
>>> g1.getNom()
'graphe1'
>>> g1.setNom('new_graphe1')
>>> g1.getNom()
'new_graphe1'
Expand source code
def setNom(self, nouveauNom) -> str:
    """Redéfinit le nom du graphe.

    Paramètres
    ----------
    nouveauNom : str - Le nouveau nom du graphe

    Tests
    -----
    >>> g1 = Graphe('graphe1')
    >>> g1.getNom()
    'graphe1'
    >>> g1.setNom('new_graphe1')
    >>> g1.getNom()
    'new_graphe1'
    """
    pass
    assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string."  # noqa : E501    
    self.nom = nouveauNom
def simplification(self) ‑> None

Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B, l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB2 = Arc('AB2',A,B,2)
>>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4)
>>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9'])
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A'])
>>> # Test sur le graphe du document de cours
>>> g1 = Graphe('graphe1')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC'])
>>> for noeud in g1.getNoeuds():
...     print(noeud)
...
Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> for noeud in g1.getNoeuds():
...     print(noeud)
...
Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
Expand source code
def simplification(self) -> None:
    """Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B,
    l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> AB2 = Arc('AB2',A,B,2)
    >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4)
    >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9])
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9'])
    >>> g.simplification()
    >>> print(g)
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A'])
    >>> # Test sur le graphe du document de cours
    >>> g1 = Graphe('graphe1')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g1.setNoeuds([A,B,C])
    >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC])
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC'])
    >>> for noeud in g1.getNoeuds():
    ...     print(noeud)
    ...
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
    >>> g1.simplification()
    >>> print(g1)
    Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    >>> for noeud in g1.getNoeuds():
    ...     print(noeud)
    ...
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
    """  # noqa : E501
    pass
    # Si A et B sont voisins alors
    # soit A a des dettes envers B, soit B a des dettes envers A.
    newArcs = []
    for noeud in self.getNoeuds():
        for voisin in noeud.getVoisins():
            detteTotale = 0
            for arc in self.getArcs():
                if (noeud == arc.getNoeudDebut() and
                        voisin == arc.getNoeudFin()):
                    # On cumule la dette
                    detteTotale += arc.getPoids()
            if detteTotale != 0:
                # On ajoute un nouvel arc avec la dette totale
                # au tableau des nouveaux arcs
                nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom()  # noqa : E501
                newArc = Arc(nouveauNom, noeud, voisin, detteTotale)
                newArcs.append(newArc)
    # Le graphe garde les mêmes noeuds
    # Mais on remplace tous les arcs
    # On commence par supprimer tous les arcs existants
    index = len(self.getArcs())-1
    while index != -1:
        self.removeArc(self.getArcs()[index])
        index -= 1
    # On ajoute les nouveaux arcs
    self.setArcs(newArcs)
    # Il faut aussi redefinir tous les voisins
    for arc in newArcs:
        if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()):
            arc.getNoeudDebut().addVoisin(arc.getNoeudFin())
    # Il faut aussi redefinir le nombre d'arcs de chaque noeud
    # On commence par mettre le nombre d'arcs à 0
    for noeud in self.getNoeuds():
        noeud.setNbArcs(0)
        # On met à jour le nombre d'arcs de chaque noeud
        for arc in newArcs:
            if noeud == arc.getNoeudDebut():
                noeud.setNbArcs(noeud.getNbArcs()+1)
            if noeud == arc.getNoeudFin():
                noeud.setNbArcs(noeud.getNbArcs()+1)