Python-Framework mesa - 2 Fragen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Ich bastele an einem Modell in mesa. Leider helfen mir an bestimmten Stellen auch intensive Recherchen und stundenlanges Experimentieren oft nicht weiter. Dies als Erklärung für meine wahrscheinlich blöden Fragen. Zu nachfolgendem Code habe ich folgende Fragen:
- In der move_to_corner- Methode komme ich ohne das break-Kommando nicht aus, wenn die beiden Agenten alternieren sollen, also im Wechsel agieren sollen. Muss das so sein?
- Die traverse_grid-Methode soll erst nach der move_to_corner-Methode aufgerufen werden. Das klappt aber nicht. Sie wird in jedem Schritt der move_to_corner-Methode aufgerufen. Wie löse ich das?
Vielen Dank im Voraus!

Code: Alles auswählen


import mesa

class Model(mesa.Model):
    """Modell mit 2 Agenten, die sich in einem Grid bewegen sollen"""
    def __init__(self, number_of_agents, width, height):
        super().__init__(number_of_agents, width, height)
        self.num_agents = number_of_agents
        self.grid = mesa.space.MultiGrid(width, height, True)
        self.schedule = mesa.time.BaseScheduler(self)

        """Nachfolgend werden 2 Agenten produziert, als Modell-Attribute definiert
         (damit  z.B. ihre Position aus dem Modell aufgerufen werden kann) und auf dem Grid platziert"""

        self.alter = Agent("alter", self)
        self.schedule.add(self.alter)
        coord = (self.random.randrange(self.grid.width), self.random.randrange(self.grid.height))
        print(f"Anfangsposition von alter: {coord}")
        self.grid.place_agent(self.alter, coord)

        self.ego = Agent("ego", self)
        self.schedule.add(self.ego)
        coord = (self.random.randrange(self.grid.width), self.random.randrange(self.grid.height))
        print(f"Anfangsposition von ego: {coord}")
        self.grid.place_agent(self.ego, coord)

    def step(self):
        """Ruft die step-Methode des Agenten auf"""
        self.schedule.step()

class Agent(mesa.Agent):
    def __init__(self, name, model):
        super().__init__(name, model)
        self.name = name

    def move_to_corner(self):
        """Bewegt die beiden Agenten von ihrer Ausgangsposition in die
        linke untere Ecke"""
        x, y = self.pos
        while x > 0 or y > 0:
            new_position = (max(x - 1, 0), max(y - 1, 0))
            print(self.name, " position: ", new_position)
            self.model.grid.move_agent(self, new_position)
            break

        self.traverse_grid()

    def traverse_grid(self):
        """Agenten sollen das gesamte Grid durchlaufen, zunächst
        aber nur Test der Position zu Beginn des Durchlaufs"""
        print(self.name, ": Position zu Beginn traverse_grid: ", self.pos)

    def step(self):
        """Wird von self.schedule.step aus dem Modell aufgerufen"""
        self.move_to_corner()

def main():
    """Produziert ein Grid"""
    model = Model(2, 5, 5)
    for _ in range(5):
        model.step()

    print("Endposition von alter:", model.alter.pos)
    print("Endposition von ego:", model.ego.pos)

if __name__ == "__main__":
    main()

__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dein Verstaendnisproblem ist recht offensichtlich: diese Methoden sind als kurz laufende Berechnungen gedacht. Eine while-Schleife kann darin nicht funktionieren, denn die dreht sich ja dauerhaft. Darum arbeitet man da mit Zustandsmaschinen. ZB kannst du die Methoden als Callbacks benutzen.

Code: Alles auswählen

class Agent(mesa.Agent):
    def __init__(self, name, model):
        super().__init__(name, model)
        self.name = name
        self._state = self.move_to_corner

    def move_to_corner(self):
        x, y = self.pos
        if x > 0 or y > 0:
            new_position = (max(x - 1, 0), max(y - 1, 0))
            print(self.name, " position: ", new_position)
            self.model.grid.move_agent(self, new_position)
         else:
            self._state = self.traverse_grid

    def traverse_grid(self):
        """Agenten sollen das gesamte Grid durchlaufen, zunächst
        aber nur Test der Position zu Beginn des Durchlaufs"""
        print(self.name, ": Position zu Beginn traverse_grid: ", self.pos)

    def step(self):
        """Wird von self.schedule.step aus dem Modell aufgerufen"""
        self._state()
Das heisst die Verschiedenen Zustaende laufen bis sie an den naechsten delegieren.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Vielen Dank, deets, für die prompte Antwort!
Zuletzt geändert von hcshm am Freitag 12. Januar 2024, 09:59, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe den Code nicht geprueft. Es ging mir um das prinzpielle Vorgehen und Verstaendnis, das bei dir nicht da war.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Erneut vielen Dank! Dieses Verständnis war bei mir tatsächlich nicht da.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@hcshm: `number_of_agents`/`num_agents` macht auch hier wieder keinen Sinn. Man würde dort für die gleiche Sache auch eher nicht zwei verschiedene Namen verwenden. Wenn `number_of_agents` zu lang ist, wäre `agent_count` etwas knapper formuliert.

Auch hier ist die Frage warum `mesa.Agent.__init__()` Argumente übergeben werden, die diese Methode einfach ignoriert.

Literale Zeichenketten sind kein Kommentar(ersatz). Die haben für die Programmiersprache an bestimmten Stellen die Funktion eines DocStrings und für einige Dokumentationswerkzeuge die gleiche Funktion auch an anderen Stellen. Hier wird mit dem „Nachfolgend …“ das `shedule`-Attribut dokumentiert, was so inhaltlich falsch ist.

Wobei der Kommentar auch überflüssig ist, weil der nur beschreibt was da recht offensichtlich schon durch den Code schon ausgesagt wird.

"alter" und "ego" als erstes Argument für den jeweiligen `mesa.Agent` ist falsch. Laut Dokumentation wird dort eine ganzzahlige ID erwartet, also irgendwo im Mesa-Rahmenwerk wird das ein Problem sein wenn da eine Zeichenkette übergeben wird.

Beim DocString von `move_to_corner()` steht was von den beiden Agenten. Das ist Wissen das man an der Stelle gar nicht haben sollte, denn diese Methode ist ja unabhängig davon wie viele Agenten es am Ende gibt, und die Klasse beschreibt das Verhalten für *ein* Exemplar.

Code: Alles auswählen

#!/usr/bin/env python3
import mesa


class Agent(mesa.Agent):
    def __init__(self, unique_id, model, name):
        super().__init__(unique_id, model)
        self.name = name
        self._step = self.move_to_corner

    def move_to_corner(self):
        """
        Bewegt den Agenten von der Ausgangsposition in die linke untere Ecke.
        """
        x, y = self.pos
        if x > 0 or y > 0:
            new_position = (max(x - 1, 0), max(y - 1, 0))
            print(self.name, "position:", new_position)
            self.model.grid.move_agent(self, new_position)
        else:
            self._step = self.traverse_grid

    def traverse_grid(self):
        """
        Agenten sollen das gesamte Grid durchlaufen, zunächst aber nur Test der
        Position zu Beginn des Durchlaufs.
        """
        print(f"{self.name}: Position zu Beginn traverse_grid: {self.pos}")
        self._step = lambda: None

    def step(self):
        self._step()


class Model(mesa.Model):
    """
    Modell mit 2 Agenten, die sich in einem Grid bewegen sollen.
    """

    def __init__(self, width, height):
        super().__init__()
        self.grid = mesa.space.MultiGrid(width, height, True)
        self.schedule = mesa.time.BaseScheduler(self)

        for id_, name in enumerate(["alter", "ego"]):
            agent = Agent(id_, self, name)
            self.schedule.add(agent)
            coord = (
                self.random.randrange(self.grid.width),
                self.random.randrange(self.grid.height),
            )
            print(f"Anfangsposition von {name}: {coord}")
            self.grid.place_agent(agent, coord)

    @property
    def agents(self):
        return self.schedule.agents

    def step(self):
        self.schedule.step()


def main():
    """
    Produziert ein Grid.
    """
    model = Model(5, 5)
    for _ in range(5):
        model.step()

    for agent in model.agents:
        print(f"Endposition von {agent.name}: {agent.pos}")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Vielen Dank! Meine Frage ist geklärt und kann entsprechend "abgehakt" werden.
Antworten