Repository: projectmesa/mesa

Issue:
RandomActivation scheduler not working after upgrade 2.1 -> 2.2.3
I upgraded 2.1 to 2.2.3 and in this simple test model RandomActivation scheduler does not work anymore: agent activation is done sequentially.
Furthermore if I not call super().__init__() in the model class, the first agent is not added to the scheduler. Is this a bug or a mistake on my part?

```python
    class CognitiveAgent(mesa.Agent):
        """Agents make decisions using automatic (e.g., reflexive) versus controlled (e.g., deliberative) cognition,
        interact with each other, and influence the environment (i.e., game payoffs)."""
    
        def __init__(self, unique_id, model):
            # Pass the parameters to the parent class.
            super().__init__(unique_id, model)
    
            # Each agent is defined by a single parameter x that represents its probability of controlled processing
            self.xdx = np.random.uniform(0., 1.)
    
        def step(self):
            # The agent's step will go here.
            # For demonstration purposes we will print the agent's unique_id
            other_agent = self.random.choice(self.model.schedule.agents)
            print(f"I'm {self.unique_id} and I choose {other_agent.unique_id}")
            
    class CognitiveModel(mesa.Model):
        """A model with some number of agents."""
    
        def __init__(self, N):
            super().__init__()
            self.num_agents = N
            self.schedule = mesa.time.RandomActivation(self)
            # Create agents
            for i in range(self.num_agents):
                a = CognitiveAgent(i, self)
                # Add the agent to the scheduler
                print(a,i)
                self.schedule.add(a)
    
        def step(self):
            """Advance the model by one step."""
            # The model's step will go here for now this will call the step method of each agent and print the agent's unique_id
            self.schedule.step() 
```


Discussion:
Thanks for reaching out. I will test this tomorrow morning, if no one else has done it by then.

> Furthermore if I not call super().**init**() in the model class, the first agent is not added to the scheduler.

Calling `super().__init__()` was always recommended, but now it’s required. It initializes `Model.agents` among other things. You’re using the Mesa Model class, so you should properly initialize it.
@rht do you have time to investigate this?
I'd need some help debugging all the problems caused by 2.1.x -> 2.2.x. Hands are full with elsewhere.
Unfortunately, I can reproduce this issue for both Mesa 2.2.0 and 2.2.3, while the correct behavior (shuffled agent steps) on Mesa 2.1.5. @projectmesa/maintainers this is a major issue and needs immediate care, we broke `RandomActivation`.

I will investigate further.
I've narrowed it down to the fact that the AgentSet's `shuffle(inplace=True)` doesn't shuffle the agents. Could be because the shuffle isn't working, or the in place updating isn't.

https://github.com/projectmesa/mesa/blob/99b35fcacadacae1d2860c86ac370f262a70da93/mesa/time.py#L144-L147

![Screenshot_366](https://github.com/projectmesa/mesa/assets/15776622/85c66aff-fe7d-4f63-8fe2-f026d46f0943)
It's the inplace updating that isn't working. Without it, it works correctly:

![Screenshot_367](https://github.com/projectmesa/mesa/assets/15776622/1d976ddb-d13e-4a1a-8e1a-af701689bd9d)

@quaquel you wrote this code and know more about the AgentSet implementation. Could assist in fixing the inplace updating?
Thanks for your work, Mesa is an amazing framework!

Resolving patch:
diff --git a/mesa/agent.py b/mesa/agent.py
--- a/mesa/agent.py
+++ b/mesa/agent.py
@@ -54,6 +54,7 @@ def __init__(self, unique_id: int, model: Model) -> None:
         except AttributeError:
             # model super has not been called
             self.model.agents_ = defaultdict(dict)
+            self.model.agents_[type(self)][self] = None
             self.model.agentset_experimental_warning_given = False
 
             warnings.warn(
diff --git a/mesa/time.py b/mesa/time.py
--- a/mesa/time.py
+++ b/mesa/time.py
@@ -143,8 +143,8 @@ def get_agent_keys(self, shuffle: bool = False) -> list[int]:
 
     def do_each(self, method, shuffle=False):
         if shuffle:
-            self.agents.shuffle(inplace=True)
-        self.agents.do(method)
+            self._agents.shuffle(inplace=True)
+        self._agents.do(method)
 
 
 class RandomActivation(BaseScheduler):


Test patch:
diff --git a/tests/test_agent.py b/tests/test_agent.py
--- a/tests/test_agent.py
+++ b/tests/test_agent.py
@@ -251,3 +251,16 @@ def test_agentset_select_by_type():
     # Test with no type specified (should select all agents)
     all_agents = agentset.select()
     assert len(all_agents) == len(mixed_agents)
+
+
+def test_agentset_shuffle():
+    model = Model()
+    test_agents = [TestAgent(model.next_id(), model) for _ in range(12)]
+
+    agentset = AgentSet(test_agents, model=model)
+    agentset = agentset.shuffle()
+    assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
+
+    agentset = AgentSet(test_agents, model=model)
+    agentset.shuffle(inplace=True)
+    assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
diff --git a/tests/test_time.py b/tests/test_time.py
--- a/tests/test_time.py
+++ b/tests/test_time.py
@@ -224,6 +224,28 @@ def test_get_agent_keys(self):
         agent_ids = {agent.unique_id for agent in model.agents}
         assert all(entry in agent_ids for entry in keys)
 
+    def test_not_sequential(self):
+        model = MockModel(activation=RANDOM)
+        # Create 10 agents
+        for _ in range(10):
+            model.schedule.add(MockAgent(model.next_id(), model))
+        # Run 3 steps
+        for _ in range(3):
+            model.step()
+        # Filter out non-integer elements from the log
+        filtered_log = [item for item in model.log if isinstance(item, int)]
+
+        # Check that there are no 18 consecutive agents id's in the filtered log
+        total_agents = 10
+        assert not any(
+            all(
+                (filtered_log[(i + j) % total_agents] - filtered_log[i]) % total_agents
+                == j % total_agents
+                for j in range(18)
+            )
+            for i in range(len(filtered_log))
+        ), f"Agents are activated sequentially:\n{filtered_log}"
+
 
 class TestSimultaneousActivation(TestCase):
     """