Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically assign unique_id to Agents #2226

Closed
wants to merge 5 commits into from

Conversation

GittyHarsha
Copy link

@GittyHarsha GittyHarsha commented Aug 20, 2024

Automatically assign unique_id to Agents. This has two goals:

  • Automatically assign agents an unique_id value on creation.
  • Guarantee that all unique_id values are actually unique.

A few design considerations:

  • The current_id counter starts now at 0, instead of 1.
    • This is consistent with for i in range(n) loops, which also assign unique_ids from 1.
  • Warnings are thrown for the most common cases of agent initialization. Other cases get an informative error.
  • The counter is kept inside the model. Testing of unique ids becomes almost impossible without that. It can be moved in another PR if desired.
  • next_id() was commonly use, so that also gets a deprecation warning.
  • The current_id counter is kept public. It can be useful to see how many agents are created in total, and is used here and there. Again, can be modified in another PR.

Usage

Instead of:

Agent(unique_id, model, ...)

You can (and should) now initialize your Agent with:

Agent(model, ...)

Resolves #2213

Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
Schelling small 🔵 -0.8% [-1.0%, -0.6%] 🔵 +1.0% [+0.9%, +1.2%]
Schelling large 🔵 -0.8% [-1.0%, -0.5%] 🔵 +0.2% [-0.6%, +0.8%]
WolfSheep small 🔵 -0.1% [-0.9%, +0.8%] 🔵 +0.0% [-0.2%, +0.3%]
WolfSheep large 🔵 -0.3% [-0.6%, -0.1%] 🔵 +0.2% [-0.2%, +0.7%]
BoidFlockers small 🔵 -2.0% [-2.6%, -1.4%] 🔵 +0.6% [+0.0%, +1.2%]
BoidFlockers large 🔵 -1.0% [-1.6%, -0.6%] 🔵 +1.8% [+1.2%, +2.5%]

@EwoutH
Copy link
Member

EwoutH commented Aug 20, 2024

This is an interesting approach, thanks!

Do you mind if I write some test cases and add them to this PR? Then we can see what edge cases are covered and for which we still have to come up with a solution.

@quaquel
Copy link
Member

quaquel commented Aug 20, 2024

Good stuf. A few comments that might be useful to move this PR forward

  1. please make sure the original message is self contained. So summarize what the PR does. I now have to follow the link to figure it out. Please still include the link for those who want to know more.
  2. As asked in the original issue (q1), the counting methods should move from the model class to the agent class and be a class method.
  3. I will try to look closely at the code later today and think about MESA 2 versus 3 and how to handle this cleanly.

@GittyHarsha
Copy link
Author

Do you mind if I write some test cases and add them to this PR? Then we can see what edge cases are covered and for which we still have to come up with a solution.

sure!

@EwoutH
Copy link
Member

EwoutH commented Aug 26, 2024

Done! I added 3 test cases, as you can see, currently one of them fails.

What helps is that most people just pass super().__init__(unique_id, model), without the keywords. That all our example models pass is also a good sign.

The test that currently fails is the one that uses all keyword arguments:

super().__init__(unique_id=unique_id, model=model)

Maybe unique_id could be another optional parameter that's None by default, but get's checked if it's passed.

Curious if you can get all three test cases simultaneously working!

@GittyHarsha
Copy link
Author

GittyHarsha commented Aug 26, 2024

@EwoutH here are the code changes I am making:

    def __init__(
        self, unique_id_or_model: int | Model = None, model: Model | None = None, unique_id: int | None = None
    ) -> None:
        """
        Create a new agent.

        Args:
            unique_id_or_model (int | Model, optional): A unique identifier for this agent or the model instance in which the agent exists.
            Defaults to None, meaning `model` and `unique_id` parameters are given as keyword arguments.
            model (Model, optional): The model instance in which the agent exists given the argument `unique_id_or_model` is a unique_id for this agent.
            Defaults to None, meaning only the model instance is given as an argument to `unique_id_or_model`.
            unique_id (int, optional): A unique identifier for this agent. Defaults to None, meaning it is not given a keyword parameter.
        """
        if unique_id is not None and model is not None:
            self.unique_id = unique_id
            self.model = model
        else:
            if isinstance(unique_id_or_model, int):
                if model is None:
                    raise RuntimeError('The second argument must be a model instance')
                else:
                    self.unique_id = unique_id_or_model 
                    self.model = model 
            elif unique_id is not None:
                self.unique_id = unique_id
                self.model = unique_id_or_model
            else:
                self.unique_id = unique_id_or_model.next_id()
                self.model = unique_id_or_model
                

        self.pos: Position | None = None

All tests are passing expect the tests in tests\test_time.py. because here the parameter unique_id_or_model is being passed a string

self = <tests.test_time.MockAgent object at 0x000001F31840B4D0>, unique_id_or_model = 'A', model = <tests.test_time.MockModel object at 0x000001F31840B440>, unique_id = None

My code changes checks only for instance int, thus it then tries to execute self.unique_id = unique_id_or_model.next_id() and thus gives error: AttributeError: 'str' object has no attribute 'next_id'

So, can strings be passed as arguments for a unique_id?
So, making changes : if isinstance(unique_id_or_model, (int, str)): should resolve it? or are there any other data types that can be passed to be assigned as unique_id which will be implicitly converted to int?

To solve this I also tried to do if not isinstance(unique_id_or_model, Model) but this doesn't work as Model cannot be imported during run time and if imported causes cyclic dependency

I can refactor the code to not include any type checking for unique_id_or_model but this will miss many cases like when both the arguments are of type Model.

@GittyHarsha
Copy link
Author

Also Instead of adding another optional paramter unique_id, just changing the name unique_id_or_model to unique_id passes all the tests, but is semantically incorrect because only a single argument of type Model will be referenced by unique_id paramter,

@quaquel
Copy link
Member

quaquel commented Aug 26, 2024

I don't see a simple solution for supporting both current style and new style Agents. Currently, unique_id and model are arguments, not keyword arguments. It might be possible to switch to Agent.__init__(first_arg, *args) and just check whether there is anything in *args. Personally, I am more inclined to just have a hard break with MESA 3. It is easy to document and easy to fix in existing models. It would also make the code here much simpler.

For my point 2 above, see the SimulationEvent code on how to handle the counting nicely. The code there is the recommended way of counting instances of a class on StackOverflow. This can be implemented in a backward compatibly way and then just let Model.next_id raise a DeprecationWarning.

@EwoutH
Copy link
Member

EwoutH commented Aug 26, 2024

I thought it would break a lot more models, but since the name of the variable in your subclass are defined by yourself and the variables names only matter when calling super(), the impact is way smaller.

The fact that all regular example models passed makes it a lot less impactful to make a breaking change here.

mesa/agent.py Outdated
@@ -35,16 +35,26 @@ class Agent:
self.pos: Position | None = None
"""

def __init__(self, unique_id: int, model: Model) -> None:
def __init__(
self, unique_id_or_model: int | Model, model: Model | None = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make it this:

Suggested change
self, unique_id_or_model: int | Model, model: Model | None = None
self, model: Model, fallback: None = None

And then warn if either model is not a Model instance or if fallback is not None.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GittyHarsha what do you think of this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to support existing codes? If so then keywordAgent cannot be supported?

Sorry, I didn't understand the purpose of fallback

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the idea.

The purpose of the fallback is to not let models that use two positional arguments (which is very common) crash hard.

So:
We support and recommend:

super().__init__(my_model)

We allow

super().__init__(unique_id, my_model)

which we convert internally to to let my_model be the model and throw unique_id away, with a warning to convert to super().__init__(my_model).

I think this will be a good balance to moving towards the future of Mesa and not breaking existing models too hard.

Copy link
Author

@GittyHarsha GittyHarsha Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self, model: Model, fallback: None = None

according to this, the order of arguments should be (model, unique_id) but existing codes use the order (unique_id, model).
Also is there a need to support keyword arguments? In that case, this doesn't work I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah so we have to handle that internally

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this is a good alternative.
To handle keyword arguments shall we handle it via **kwargs?

self, model: Model, fallback: None = None, **kwargs

And check if the unique_id keyword is present and handle it accordingly.

Copy link
Author

@GittyHarsha GittyHarsha Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented your suggestions:

   _ids = itertools.count()

    def __init__(
       self, model: Model, fallback: None = None, **kwargs
    ) -> None:

        if "unique_id" in kwargs:
            self.unique_id = kwargs["unique_id"]
            self.model = model 
        elif fallback is None:
            self.unique_id = next(self._ids)
            self.model = model 
        else:
            self.unique_id = model 
            self.model = fallback

only one test is failing

     super().__init__(unique_id, model=model)
E       TypeError: Agent.__init__() got multiple values for argument 'model'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I don't care too much about that test case, it won't happen too often.

I do want to use the next(self._ids) always, so just ignore the unique_id inputted by the user.

@GittyHarsha
Copy link
Author

GittyHarsha commented Aug 27, 2024

    _ids = itertools.count()

    def __init__(
       self, unique_id_or_model: int | Model | None = None, fallback: Model | None = None, **kwargs
    ) -> None:

        if "unique_id" in kwargs:
            if "model" in kwargs:
                self.unique_id = kwargs["unique_id"]
                self.model = kwargs["model"]
            else:
                self.unique_id =kwargs["unique_id"]
                self.model = unique_id_or_model
        elif "model" in kwargs:
            self.unique_id = unique_id_or_model
            self.model = kwargs["model"]
        elif fallback is None:
            self.unique_id = next(self._ids)
            self.model = unique_id_or_model
        else:
            self.unique_id = unique_id_or_model 
            self.model = fallback

this passes all the tests.

@EwoutH
Copy link
Member

EwoutH commented Aug 28, 2024

Do you mind if I make some modifications on your branch? You made a great start but I would like to tidy it up myself.

@GittyHarsha
Copy link
Author

GittyHarsha commented Aug 28, 2024

Sure! No problem. I learnt a lot trying to solve this issue, especially as someone new to open source

@EwoutH EwoutH changed the title Fix issue #2213 Automatically assign unique_id to Agents Aug 29, 2024
Co-Authored-By: HarshaNP <96897754+gittyharsha@users.noreply.github.com>
@EwoutH EwoutH added feature Release notes label breaking Release notes label labels Aug 29, 2024
They assumed starting at 0, we now start at 1
@EwoutH EwoutH marked this pull request as ready for review August 29, 2024 18:57
@EwoutH
Copy link
Member

EwoutH commented Aug 29, 2024

@projectmesa/maintainers Ready for review!

Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔴 +81.4% [+79.2%, +83.7%] 🔵 -0.7% [-0.8%, -0.5%]
BoltzmannWealth large 🔴 +74.7% [+33.5%, +125.5%] 🔵 -0.2% [-2.7%, +2.4%]
Schelling small 🔴 +44.1% [+43.7%, +44.6%] 🔵 -0.6% [-0.9%, -0.4%]
Schelling large 🔴 +52.6% [+51.8%, +53.4%] 🔵 +0.8% [-0.1%, +1.9%]
WolfSheep small 🔴 +77.6% [+75.7%, +79.4%] 🔴 +15.2% [+14.8%, +15.6%]
WolfSheep large 🔴 +76.0% [+74.9%, +77.0%] 🔴 +23.7% [+22.7%, +24.5%]
BoidFlockers small 🔴 +38.5% [+37.7%, +39.3%] 🔵 -0.7% [-1.3%, -0.1%]
BoidFlockers large 🔴 +40.5% [+39.2%, +41.6%] 🔵 -0.9% [-1.5%, -0.4%]

self.current_id += 1
return self.current_id
return return_id

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my opinion, this is part of the behavior of the Agent class so it is strange that it is located in the model class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but let's move it in a separate PR. It breaks practically all tests with more than one test class or function in a file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I fully understand your point. Why not move the counting code into Agent in this PR?

I haven't looked at how bad this would be in terms of tests, but it seems logical to include it here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy my branch and move it. You will see what happens, and maybe you have an elegant solution for it.

"The model parameter is required to initialize an Agent object. Initialize the agent with Agent(model, ...)."
)

self.unique_id = self.model._next_id
self.pos: Position | None = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to have a closer look tomorrow. I think it might be possible to ensure backward compatibility in a more elegant way.

@EwoutH
Copy link
Member

EwoutH commented Aug 29, 2024

The benchmarks are slower because they still use the old structure which creates a huge amount of warnings.

If they stop throwing warnings, it goes probably faster.
@EwoutH
Copy link
Member

EwoutH commented Aug 29, 2024

This might actually be the largest breaking change we make in Mesa 3.0. I hope it's worth it.

There was some beauty in explicitly knowing that you assigned each agent and unique ID. It was one of the first things you had to teach. It might get used a lot less when that isn't automatic anymore.


I still dislike having the example models in two places. It's maintenance intensive.

@EwoutH EwoutH added trigger-benchmarks Special label that triggers the benchmarking CI and removed trigger-benchmarks Special label that triggers the benchmarking CI labels Aug 29, 2024
Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔴 +32.7% [+30.4%, +34.8%] 🔵 +1.8% [+1.6%, +1.9%]
BoltzmannWealth large 🔵 +25.3% [-9.8%, +66.6%] 🔵 +0.8% [-1.5%, +3.4%]
Schelling small 🔴 +9.9% [+9.6%, +10.3%] 🔵 +1.0% [+0.7%, +1.2%]
Schelling large 🔴 +11.0% [+10.2%, +11.8%] 🔵 +2.2% [-0.3%, +4.7%]
WolfSheep small 🔴 +15.6% [+14.5%, +16.8%] 🔴 +5.6% [+5.3%, +5.9%]
WolfSheep large 🔴 +15.1% [+14.1%, +16.5%] 🔴 +8.7% [+5.6%, +12.4%]
BoidFlockers small 🔴 +15.4% [+14.9%, +15.9%] 🔵 -0.0% [-0.7%, +0.7%]
BoidFlockers large 🔴 +16.1% [+15.3%, +17.1%] 🔵 +1.4% [+0.7%, +2.1%]

@wang-boyu
Copy link
Member

A few comments from my side (open for discussions):

  1. In the past I used uuid module to create unique agent ids, such as here. In this way we don't need a counter in Model, so we can get rid of model.next_id() as well as model.current_id.

  2. What if users want to explicitly set agent ids by themselves? For instance, I may set agent ids to be their unique names. It might be better to keep the unique_id parameter of the Agent class optional.

    • If unique_id is provided, agent.unique_id is set accordingly. This is current behavior.
    • Otherwise (i.e., unique_id = None), as a default behavior, Mesa creates a unique agent id automatically.
  3. It may be useful to check for uniqueness of agent ids. For example, we could have a model._agent_id_set attribute that is a set. While creating agents, we check whether new agent id is already taken and raise exception.

@quaquel
Copy link
Member

quaquel commented Aug 30, 2024

A few comments from my side (open for discussions):

  1. In the past I used uuid module to create unique agent ids, such as here. In this way we don't need a counter in Model, so we can get rid of model.next_id() as well as model.current_id.

Regardless of how it is done, it should move the Agent class.

I am not familiar with uuid but is that not overkill for the function it has in MESA? The basic function of unique_id is to serve as a unique identifier for indexing agents when collecting agent level data. Why would we need a 128-bit identifier for this? A possibly convenient side-effect of counting is that you know how many agents have been created over the runtime of the model.

  1. What if users want to explicitly set agent ids by themselves? For instance, I may set agent ids to be their unique names. It might be better to keep the unique_id parameter of the Agent class optional.
    • If unique_id is provided, agent.unique_id is set accordingly. This is current behavior.
    • Otherwise (i.e., unique_id = None), as a default behavior, Mesa creates a unique agent id automatically.

unique_id is necessary for the functioning of MESA (e.g., data collection). So, analogous to step, we should do it automatically. If the user wants her own id, they can do so with their own attribute.

  1. It may be useful to check for uniqueness of agent ids. For example, we could have a model._agent_id_set attribute that is a set. While creating agents, we check whether new agent id is already taken and raise exception.

If you make it part of MESA, uniqueness is guaranteed by construction. Messing with internal attributes of classes is at your own peril. So I don't see a need to be so defensive in how we code it up. If we want to be defensive, you could move unique_id into _unique_id and have a property for unique_id and a setter that raises some appropriate exception.

@Corvince
Copy link
Contributor

First of all lets not again discuss this PR to its death. Great work by @GittyHarsha and @EwoutH. I think making it automatic by default is very good and the main motivation for this PR. Everything else can be discussed separately.

  1. In the past I used uuid module to create unique agent ids, such as here. In this way we don't need a counter in Model, so we can get rid of model.next_id() as well as model.current_id.

I did the same, but I think its overkill. UUID is often used to make the ids universally unique and to prevent guessing the next id. We don't need either of those. As a side effect a counter even conveys additional information, for example the order in which agents were created. Its also much easier for humans to track a number than a uuid.

  1. What if users want to explicitly set agent ids by themselves? For instance, I may set agent ids to be their unique names. It might be better to keep the unique_id parameter of the Agent class optional.

I think its unnecessary. For us maintainers we now know for certain that the unique_id is truly unique. Before this PR I was always reluctant to fully rely on this.
For users I don't see the benefit of overwriting unique_id. E.g. If you already have the name just use it as name, not id. And more importantly if users want to use their own id, they can still simply overwrite the unique_id attribute.

  1. It may be useful to check for uniqueness of agent ids. For example, we could have a model._agent_id_set attribute that is a set. While creating agents, we check whether new agent id is already taken and raise exception.

Again, we don't need this if unique_id is not, but automatically assigned.

Regardless of how it is done, it should move the Agent class.

Partly agree. At first I thought we should move it to the mesa or mesa.utils namespace and make it generally available. But its nice that it only counts agents. But still I don't think it should be part of the agent class, but the agent namespace. And we can just expose an instance of itertools.count

@quaquel
Copy link
Member

quaquel commented Aug 30, 2024

Partly agree. At first I thought we should move it to the mesa or mesa.utils namespace and make it generally available. But its nice that it only counts agents. But still I don't think it should be part of the agent class, but the agent namespace. And we can just expose an instance of itertools.count

I would be fine with that as well. In SimulationEvent, I used a class based approach based on the most upvoted StackOverflow answer, but I am fine either way. The main point for me is that it is not part of the behavior of Model so it should be removed from there (here or in a next PR).

@EwoutH
Copy link
Member

EwoutH commented Aug 30, 2024

I just want to add: I try to keep my PRs small in scope and if possible even atomic.

I know there's benefit into doing things right once, but sometimes that makes PRs unmanageable big, either code wise or, more often, conceptually (which is why we don't have a new datacollector yet).

This PR modifies one thing: Automatically increasing the unique_id. While I agree that should be ideally done in the Agent, it would make this PR way too big. In two ways:

  • It introduces a large conceptual change that unique_ids are not per Model, but per Agent superclass.
  • It breaks a lot of tests that rely on the Agent count starting at 0 when a new model is initialized. So if you define multiple models (like you do in test classes) is messes a lot of stuff up.

image

So again, I'm not fundamentally against any of these changes. It just are more changes that are better fitted in a new PR.

@EwoutH EwoutH mentioned this pull request Aug 30, 2024
7 tasks
@wang-boyu
Copy link
Member

Thanks for your responses @quaquel @Corvince! Some further comments -

Regarding implementation details of unique_id

I don't really care if it's using uuid or a counter. I used uuid because it's less work for me. In this PR, if we're using a counter, is it possible to remove model.next_id() and model.current_id, or make them private? There appear to be implementation details of unique_id and shouldn't be exposed to users. In addition, they may easily cause breaking changes in the future if we'd like to change anything.

A possibly convenient side-effect of counting is that you know how many agents have been created over the runtime of the model.

I wouldn't count on these because agents can be removed/destroyed anytime. We then need to differentiate how many agents were created and how many agents are still alive.

As a side effect a counter even conveys additional information, for example the order in which agents were created.

unique_id are supposed to be unique identifiers, nothing more, nothing less. I don't really like the ideas of having side effects that rely on implementation details. Sorry : )

Regardless of how it is done, it should move the Agent class.

I agree. But if it's using a counter, then the counter has to be stored somewhere outside individual agents. Keeping it inside a model makes sense. However, this is also why I think using uuid is better - it eliminates the need of maintaining a counter by ourselves.

Regarding user-settable unique_id

The central question is: why are users not allowed to use their own unique ids?

My suggestions are:

  • By default, we (Mesa) create unique ids so users don't have to worry about them.
  • Advanced users can use their own unique ids. Mesa validates uniqueness to make sure they don't screw up.

As an example, Mesa-Geo sets unique_id of agents when batch creating geo-agents from geodataframe: https://github.com/projectmesa/mesa-geo/blob/9c3196c932be94fdc6ead4a599740445c56d63bd/mesa_geo/geoagent.py#L198-L200

@quaquel
Copy link
Member

quaquel commented Aug 30, 2024

I ran a quick performance test on just 1000 agents, UUID vs. itertools.count

Screenshot 2024-08-30 at 13 44 55

I just don't see the need for UUID in this context, and it comes with a real performance difference.

In this PR, if we're using a counter, is it possible to remove model.next_id() and model.current_id, or make them private? There appear to be implementation details of unique_id and shouldn't be exposed to users.

I agree but there is @EwoutH point on how this causes all tests to fail. I want to take a closer look myself at this later today. I think it is possible to move everything into Agent, while raising deprecation warnings from the old structure.

I agree. But if it's using a counter, then the counter has to be stored somewhere outside individual agents.

In my screenshot, _ids is a class attribute, so it is not stored inside the individual agents. Given that it is part of the behavior of the Agent class, it makes sense it is either done at the class level (my preference) or at least part of the Agent namespace (as suggested by @Corvince).

The central question is: why are users not allowed to use their own unique ids?

This is perfectly analogous to the discussion on automatically counting steps. Users can still do this, but just not by using the unique_id attribute. MESA relies on unique_id for agent data collection, so it makes sense that MESA internally handles this correctly. As a minor point, it was deeply confusing to many of my students why they had to supply the unique_id themselves.

@quaquel
Copy link
Member

quaquel commented Aug 30, 2024

@EwoutH, you might want to check #2260. This is how I would go about solving the issue. I don't have any existing tests failing, while unique_id counting is done inside the Agent class. #2260 is, of course, not complete. For example, tests are still needed, and I would add a deprecation warning inside Model. Am I missing something important? I fail to understand what is so complicated about any of this.

@EwoutH
Copy link
Member

EwoutH commented Aug 30, 2024

@Corvince you’re spot on. That’s what I discovered when I tried to move it. That’s where all the tests were failing. That’s why I didn’t move it.

And I explained this I think 5 times now.

Sorry guys, but I’m getting a bit done with the “whataboutism” this way we never get anything done.

We’re all experienced developers. We have to trust each other and have to give each other a bit of room. If you really care that much open a follow-up PR yourself.

@wang-boyu
Copy link
Member

Just for the record, my comments are not blocking. I remembered we discussed this before (correct me if I'm wrong), if at least two maintainers approve a PR then it's considered mergable.

@EwoutH
Copy link
Member

EwoutH commented Aug 30, 2024

I’m resigning as owner of this specific effort. Feel free to work further on this branch.

@Corvince
Copy link
Contributor

unique_id are supposed to be unique identifiers, nothing more, nothing less. I don't really like the ideas of having side effects that rely on implementation details. Sorry : )

Isn't this contradictory to the rest of your argument? ;)

If this is the case, and I basically agree with this, then why make so much fuss about being able to change them? If it's just for identification, one can be happy that it happens automatically, no need to assign custom names or anything else to them.

What I meant with positive side effects that you can eyeball some information from them. Nothing to rely on, but a good estimate about the age of the agent and if you are tracking the same agent in multiple places

quaquel added a commit to quaquel/mesa that referenced this pull request Sep 4, 2024
makes this consistent with projectmesa#2226
quaquel added a commit to quaquel/mesa that referenced this pull request Sep 4, 2024
makes this consistent with projectmesa#2226
quaquel added a commit that referenced this pull request Sep 4, 2024
This PR ensures that the unique_id in Agent is assigned automatically. It draws on work done in #2226. In short, this PR makes 3 changes:

Agent.unique_id is assigned automatically and no longer used as an argument to Agent. A deprecation warning is issued if the Agent is instantiated with 2 arguments (unique_id and model). The value passed for unique_id is ignored in favor of the new system. This last point is in line with Automatically assign unique_id to Agents #2226.
Model.next_id() raises a deprecation warning and always return 0.
unique_id is unique relative to a Model instance. Internally, Agent has a class-level attribute _ids. This attribute is a dictionary with the Model instance as key and the "generator" for unique_id as value.
All unit tests and benchmarks work with the new structure. They raise no deprecation warnings.

examples, tutorials, etc., still need to be updated to be consistent with this change.
@quaquel
Copy link
Member

quaquel commented Sep 4, 2024

closing as solved via #2260

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Release notes label feature Release notes label
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automate and enforce unique unique_id in Agents
5 participants