JuliaDynamics / Agents.jl

Agent-based modeling framework in Julia
https://juliadynamics.github.io/Agents.jl/stable/
MIT License
736 stars 119 forks source link

Conflict between nearest_neighbor() function and euclidean distance for agent pair. #1043

Closed sahil-khan11 closed 3 months ago

sahil-khan11 commented 3 months ago

Issue The nearest neighbor for a given agent within the radius "r" doesn't align with the Euclidean distance between the agent and its nearest neighbor. The following example shows that the distance between neighbors is quite big compared to r used in the nearest_neighbor() function.

Minimal Working Example


@agent struct SocialAgent(ContinuousAgent{2, Float64})
    mass::Float64
end

function ball_model(; speed = 0.002)
    space2d = ContinuousSpace((1, 1); spacing = 0.02)
    model = StandardABM(SocialAgent, space2d; agent_step!, properties = Dict(:dt => 1.0),
                        rng = MersenneTwister(42))
    # And add some agents to the model
    for ind in 1:500
        pos = Tuple(rand(abmrng(model), 2))
        vel = sincos(2π * rand(abmrng(model))) .* speed
        add_agent!(pos, model, vel, 1.0)
    end
    return model
end

agent_step!(agent, model) = move_agent!(agent, model, model.dt)

function model_step!(model)
    for (a1, a2) in interacting_pairs(model, 0.01, :nearest)
        elastic_collision!(a1, a2, :mass)
    end
end

model = ball_model()

#### Checking euclidean distance for nearest pairs. 
r = 10^-100
euclidean_distance(model[3].pos, nearest_neighbor(model[3], model, r).pos, model)

Output

0.02637710352142322

Agents v6.0.12 Julia v1.10.1

Datseris commented 3 months ago

yeah this shouldn't find any neighbor, it should return nothing in nearest_neighbors. Probably a bug.

sahil-khan11 commented 3 months ago

@Datseris What do you think about this? I was thinking maybe replacing nearest_neighbor() in interaction_pair() with this logic. At the moment it compares all partners in a given radius based on Euclidean distance and gives out one that is nearest to the agent within that given radius. It can be modified to give all neighbors for a given radius too.

r = 0.5
agent = model[27]
partner  = agent               #To create a similar type of variable
partner_dist = 10.0           #Variable with some random big number for saving and comparing new partner distances
z = partner_dist                 #For return verification 

for id in allids(model)

    if id != agent.id
        x = euclidean_distance(agent.pos, model[id].pos, model)

        if x <= r && x <= partner_dist
            partner_dist = x
            partner = model[id]
        end
    end

    if partner_dist == z
        return nothing
    else
        return (partner, partner_dist)
    end

end

It doesn't look elegant at the moment but I think it does the work :)

Some outputs here:

with r = 1

(SocialAgent(56, [0.4123437422626348, 0.8654172979541885], [0.0014994958181429924, -0.0013234471245092032], 1.0), 0.11955423401462202)

with r = 0.7

(SocialAgent(56, [0.4123437422626348, 0.8654172979541885], [0.0014994958181429924, -0.0013234471245092032], 1.0), 0.11955423401462202)

with r = 0.5

(SocialAgent(56, [0.4123437422626348, 0.8654172979541885], [0.0014994958181429924, -0.0013234471245092032], 1.0), 0.11955423401462202)

with r = 0.1

nothing

with r = 10^-100

nothing

Let me know what you think :)

Tortar commented 3 months ago

I'm not on my computer to test this, but I think this can be fixed simply by using nearby_ids_exact instead of nearby_ids in nearest_neighbors

sahil-khan11 commented 3 months ago

Yes, it seems nice. I just checked but there is a difference that nearby_ids() doesn't count self-agent which is the point of reference while nearby_ids_exact() does. How it can be avoided? because it will be an issue otherwise.

Tortar commented 3 months ago

I don't think that it is true, nearby_ids_exact just filters out nearby_ids

Tortar commented 3 months ago

Fixed in #1044