Sheep herding: A Real-Time Agent-Based Simulation

AreaSize = 150.;
NumberOfSheep=46;
Shepherd=2.;
Sheep=1.;
Barn={0,0};

dummyPoint={-1.,-1.};

initialAgents:=Developer`ToPackedArray[
Table[
If[i==0,
Join[{Shepherd}, (* type *)
Barn,(* Location *)
AngleVector[RandomReal[{-Pi,Pi}]] (* heading *)
(* {speed} *)
],
Join[
{Sheep},
RandomReal[{AreaSize/2.,AreaSize},2],(* Location *)
AngleVector[RandomReal[{-Pi,Pi}]] (* heading *)
]],
{i,0,NumberOfSheep}
]
]

visualize[agents_,ra_,Pd_,Pc_]:=Module[{
shepherdsP=Take[#,{2,3}]&/@Select[agents,(#[[1]]==Shepherd)&],
sheepP=Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&],
GCM=Mean[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]],
N=Length[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]],
furthestSheep=SortBy[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&], -Norm[#-Mean[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]]] &][[1]],
dummyListP={dummyPoint}},
Show[
(* Plotting barn *)
Graphics[{EdgeForm[{Thick,RGBColor[0.5,0.86,0.5]}],White,Rectangle[Barn-{2,2},Barn+{2,2}]}],

(* Plotting sheep *)
Graphics[{
AbsolutePointSize[10],RGBColor[.9,.42,.17],
Point@If[shepherdsP=={},dummyListP,shepherdsP]
}],

(* Plotting shepherds *)
Graphics[{
AbsolutePointSize[10],RGBColor[.48,.11,.56],Opacity[.5],
Point@If[sheepP=={},dummyListP,sheepP]
}],

(* Plotting GCM *)
Graphics[{Locator[GCM]}],

(* Plotting furthest sheep *)
Graphics[{EdgeForm[{Thin,Dashed}],FaceForm[],Disk[GCM,ra (N^(2/3))]}],

(* Plotting driving position *)
If[Norm[furthestSheep-GCM]<ra (N^(2/3)),Graphics[{EdgeForm[{Thick,RGBColor[0.91,0.5,0.13]}],White,Rectangle[GCM+Normalize[GCM-Barn]Pd-{2,2},GCM+Normalize[GCM-Barn]Pd+{2,2}]}],

(* Plotting collecting position *)
Graphics[{AbsolutePointSize[10],RGBColor[1,0.14,0.15],Point@furthestSheep},{EdgeForm[{Thick,RGBColor[1,0.14,0.15]}],FaceForm[],Rectangle[furthestSheep+Normalize[furthestSheep-GCM]Pc-{2,2},furthestSheep+Normalize[furthestSheep-GCM]Pc+{2,2}]}]],

ImageSize->{375,375},
AspectRatio->Automatic,
Frame->False,
Axes->False,
PlotRange->{{-.5 AreaSize,1.5 AreaSize},{-.5 AreaSize,1.5AreaSize}}
]
]

updateAgents[agents_,n_,rs_, ra_,ρa_,c_, ρs_, h_, e_, δ_, p_,δs_,Pd_,Pc_]:=Module[{
N=Length[Select[agents,(#[[1]]==Sheep)&]],
nearestSheep=Nearest[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]],
nearestShepherd=Nearest[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Shepherd)&]],
GCM=Mean[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]],
furthestSheep=SortBy[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&], -Norm[#-Mean[Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep)&]]] &][[1]],
i,Ai,Hi,δi,ϵi,Ria,Ris,Ci,metricNeighbours,topologicNeighbours,ξ,LCMi,Di,Ain,Hin
},

Developer`ToPackedArray[
Table[
Ai=agents[[i,{2,3}]];
Hi=agents[[i,{4,5}]];

Hin=Hi;
Ain=Ai;

If[Norm[GCM-Barn]>2,
(* task not completed *)
If[agents[[i,1]]==Sheep,
(* sheep's behavior *) 
ϵi={0,0};
Ria={0,0};
Ris={0,0};
Ci={0,0};
δi=δ;

metricNeighbours=Take[#,{2,3}]&/@Select[agents,(#[[1]]==Sheep & Norm[Ai-#[[1]]]<ra )&];
If[Length@metricNeighbours>0,
Ria=Normalize[Sum[(metricNeighbours[[j]]-Ai)/Norm[metricNeighbours[[j]]-Ai],{j,1,Length@metricNeighbours}]];
];

If[Norm[nearestShepherd[Ai][[1]]-Ai]>rs,
(* random grazing *)
If[RandomReal[]<p, 
ξ=RandomReal[{-Pi, Pi}];
ϵi=AngleVector[ξ];,
δi=0;
];,

(* nearestShepherd < rs *)
Ris=Normalize[Ai-nearestShepherd[Ai][[1]]];

topologicNeighbours=nearestSheep[Ai,n];
If[Length@topologicNeighbours>0,
LCMi=Mean[topologicNeighbours];
Ci=Normalize[LCMi-Ai];
];
];

Hin=Normalize[h Hi+c Ci+ρa Ria+ρs Ris+e ϵi];
Ain=Ai+δi Hin;,
 
(* shepherds' behavior *)
Ci=Hi;(* heading for collecting *)
Di=Hi;(* heading for driving *)
ϵi={0,0};
δi=δs;

(* random noise to match sheep random noise *)
If[RandomReal[]<p, 
ξ=RandomReal[{-Pi, Pi}];
ϵi=AngleVector[ξ];];

If[Norm[nearestSheep[Ai][[1]]-Ai]<3ra,
(* too close to anyone, stop *)
δi=0;,

(* sheep packed, drive them *)
If[Norm[furthestSheep-GCM]<ra (N^(2/3)),
Di=(GCM+Normalize[GCM-Barn]Pd)-Ai;,

(* sheep dispersed, collect them *)
Ci=(furthestSheep+Normalize[furthestSheep-GCM]Pc)-Ai;
];
];

Hin=Normalize[h Hi+Ci+Di+e ϵi];
Ain=Ai+δi Hin;
];
];

Join[{agents[[i,1]]},Ain ,Hin]

,{i,Length[agents]}
]]
]

Manipulate[
Refresh[
If[moving,ag=updateAgents[ag,n,rs, ra,ρa,c, ρs, h, e, δ, p,δs,ra √NumberOfSheep,ra]];
visualize[ag,ra,ra √NumberOfSheep,ra]
,UpdateInterval->If[moving,0,Infinity]
]
,"Sheep"
,{{n,NumberOfSheep,"number of nearest neighbours"},0,NumberOfSheep,1,ImageSize->Tiny}
,{{rs,65,"shepherd detection distance"},1,AreaSize,ImageSize->Tiny}
,{{ra,2,"sheep to sheep interaction distance"},0,AreaSize,ImageSize->Tiny}
,{{ρa,2,"relative strength of repulsion from other sheep"},0,10,ImageSize->Tiny}
,{{c,1.05,"relative strength of of attraction to nearest neighbours"},0,10,ImageSize->Tiny}
,{{ρs,1,"relative strength of repulsion from shepherd"},0,10,ImageSize->Tiny}
,{{h,0.5,"relative strength of proceeding in previous direction"},0,10,ImageSize->Tiny}
,{{e,0.3,"relative strength of angular noise"},0,10,ImageSize->Tiny}
,{{δ,1,"displacement per step"},0,10,ImageSize->Tiny}
,{{p,0.05,"probability of moving while grazing"},0,1,ImageSize->Tiny}
,"Shepherd"
,{{δs,1.5,"displacement per step"},0,10,ImageSize->Tiny}
," "
,{{moving,False,"run simulation"},{True, False}}
,{{ag,initialAgents},ControlType->None}
," "
,Button["Step",moving=False;ag=updateAgents[ag,n,rs, ra,ρa,c, ρs, h, e, δ, p,δs,ra √N,ra],ImageSize->Medium]
,Button["Reset",moving=False;ag=initialAgents,ImageSize->Medium]
,SaveDefinitions->True,AutorunSequencing->{6},TrackedSymbols:>{moving,ag},
SynchronousUpdating->True,ControlPlacement->Left
]

This Demonstration simulates the dynamics of sheep herding (shepherd in orange and sheep in purple). An individual of each species is simulated as a particle moving in with respect to the rules defined in the paper presented by Strömbom, et al. (2014), doi: 10.1098/rsif.2014.0719. The initial condition is such that there are 46 sheep randomly distributed in the upper left quadrant of the arena and the shepherd in the barn (bottom left corner of the arena). The shepherd's task is to herd the sheep into the barn (green rectangle). During this process it switches between collecting sheep (when these are overly dispersed, marked by the dashed circle) and driving them towards the barn. Depending on the parameter settings, the shepherd's task is more or less simple. Its success rate highly depends on the number of nearest neighbours individual sheep take into account when performing their decisions. You can stop and restart the real-time simulation using the "run simulation" checkbox.

D. Strömbom, et al. "Solving the shepherding problem: heuristics for herding autonomous, interacting agents," J. R. Soc. Interface, 11 (2014) 20140719; doi: 10.1098/rsif.2014.0719.

Contributed by: Iztok Lebar Bajec