gama-platform / gama.old

Main repository for developing the 1.x versions of GAMA
GNU General Public License v3.0
304 stars 99 forks source link

[GAML] on_change with the "legacy/deprecated" way of defining its action does not warn when the wrong action is being called #3784

Closed chapuisk closed 1 year ago

chapuisk commented 1 year ago

Describe the bug

I am afraid there is no simple way to "clearly and concisely" describe the bug. I'de try to reproduce this issue in a simple model but failed. In few worlds: I attached behavior of the model to a slider parameter, with on_change facet pointing toward an action of the world that changes agent attributes. It lead to an error that stops the simulation.

To Reproduce

  1. go to Library models > Toy Models > Ants (Foraging and Sorting) > models > Ant Foraging.gaml
  2. put on_change on evaporation_per_cycleparameter (works exactly the same with diffusion_rate)
  3. create an action that does nothing in global, e.g. action donothing {}
  4. populate on_change with the previously created action, e.g. on_change: donothing
  5. run the model, change slider value, see the error.

Expected behavior Makes it work properly or prevent the use of on_change over float parameters.

Desktop (please complete the following information):

Additional context

The error message is a bit different in my case: working on one of my model with agent having a string variable to "A" or "B" that drives they behavior. I use the on_change facet to dynamically update the proportion of agent being in state A or B: a float parameter from 0 to 1 is read by global that update the proportion of agent being A or B. When I play with it while the simulation of the model is running, I got an error nil value detected. Here is my model:

global {

    string COWARD <- "coward";
    string HERO <- "hero";
    list behavior <- [COWARD,HERO];
    map<string,rgb> behavior_color <- [COWARD::#darkred,HERO::#darkgreen];

    float coward_x_heroes <- 0.0 parameter:true min:0.0 max:1.0 on_change:update_state;

    init { create people number:100; do update_state; do ufe; }

    action update_state {
        list cowards <- int(length(people) * (1-coward_x_heroes)) among people;  
        ask cowards { state <- COWARD; }
        ask people - cowards { state <- HERO; }
    }

    action ufe { ask people {do choose_a_friend; do choose_an_enemy;}}
}

species people skills:[moving] {

    people friend;
    people enemy;

    string state;

    reflex move { do goto target:choose_a_target(); }

    point choose_a_target {
        switch state {
            match COWARD {return {friend.location.x + (friend.location.x - enemy.location.x) / 2,
                    friend.location.y + (friend.location.y - enemy.location.y) / 2};}
            match HERO {return {(friend.location.x + enemy.location.x) / 2,
                    (friend.location.y + enemy.location.y) / 2};}
        }
    }

    action choose_a_friend { friend <- any(people-self-friend-enemy); }
    action choose_an_enemy { enemy <- any(people-self-friend-enemy); }

    aspect default { draw triangle(1#m) color: behavior_color[state] rotate: heading + 90.0; }
}

experiment xp {
    user_command "Randomize" color:#darkblue {ask world {do ufe();}}
    user_command "Relocate" color:#darkblue {ask people {location <- any_location_in(world.shape);}}

    float minimum_cycle_duration <- 0.02;

    output { display main {species people;}}
}
AlexisDrogoul commented 1 year ago

Actually, the way you are calling the action in on_change: is accepted, but it should warn you or even make an error at compile time -- and it doesn't, that's where the issue lies -- about the fact that you mix up the action you are running between the simulation(s) and the experiment.

When defined on an attribute of an agent (simulation, experiment, regular agent), the action should be declared in this agent. This is why, if you declare on_change: on a parameter in an experiment, it will fail to run the action in the simulation (it's logic : in case of multiple simulations, which one should be picked ?). This is the cause of the error you are having. In any case, this way of calling actions should not be demonstrated/used too much, as it has a number of flaws.

Better use the block syntax, e.g. on_change: {ask simulations {do donothing();}} or directly the block at the end, if you want to run it on the simulations managed by this experiment. Or use simulation to get the one managed by the parameters panel. Or put the action in experiment and simply write on_change: {do donothing();}.

You have an example in Building Elevation.gaml on how it can be used to do pretty cool things.

After that, I did not look at the second model as it seems a different issue, but keep in mind that playing with on_change: too much while a simulation is running will inevitably raise errors due to the concurrency between the different operations. It should be used with parsimony, for instance to make sure that parameters remain coherent with one another, etc.