├── Schelling ├── Mesa │ ├── __init__.py │ ├── benchmark_schelling.py │ └── Schelling.py ├── Agents │ ├── benchmark_schelling.jl │ └── Schelling.jl ├── Mason │ ├── benchmark_schelling.sh │ ├── Agent_large.java │ ├── Agent_small.java │ ├── Schelling_small.java │ └── Schelling_large.java ├── NetLogo │ ├── benchmark_schelling.sh │ ├── parameters_schelling.xml │ └── Schelling.nlogo └── DECLARATION.md ├── WolfSheep ├── Mesa │ ├── __init__.py │ ├── benchmark_wolfsheep.py │ ├── WolfSheep.py │ └── agents.py ├── Agents │ ├── benchmark_wolfsheep.jl │ └── WolfSheep.jl ├── Mason │ ├── benchmark_wolfsheep.sh │ ├── Sheep_large.java │ ├── Sheep_small.java │ ├── Wolf_large.java │ ├── Wolf_small.java │ ├── Wsg_small.java │ └── Wsg_large.java ├── NetLogo │ ├── benchmark_wolfsheep.sh │ ├── parameters_wolfsheep.xml │ └── WolfSheep.nlogo └── DECLARATION.md ├── mason.22.jar ├── .gitignore ├── Project.toml ├── .github ├── run-flocking.sh ├── run-wolfsheep.sh ├── run-schelling.sh ├── run-forestfire.sh ├── setup.sh └── workflows │ └── run-benchmarks.yml ├── seed_netlogo.jl ├── runall.sh ├── Flocking ├── Agents │ ├── benchmark_flocking.jl │ └── Flocking.jl ├── Mesa │ ├── benchmark_flocking.py │ ├── Flocking.py │ └── boid.py ├── Mason │ ├── benchmark_flocking.sh │ ├── Flocking_large.java │ ├── Flocking_small.java │ ├── Flocker_large.java │ └── Flocker_small.java ├── NetLogo │ ├── benchmark_flocking.sh │ ├── parameters_flocking.xml │ └── Flocking.nlogo └── DECLARATION.md ├── create_benchmark_table.jl ├── LICENSE.md └── README.md /Schelling/Mesa/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /WolfSheep/Mesa/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mason.22.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaDynamics/ABMFrameworksComparison/HEAD/mason.22.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Manifest.toml 2 | *.pyc 3 | *.code-workspace 4 | *.class 5 | .*.tmp.nlogo 6 | netlogo 7 | benchmark_results.txt 8 | __pycache__ 9 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Agents = "46ada45e-f475-11e8-01d0-f70cc89e6671" 3 | BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" 4 | LightXML = "9c8b4983-aa76-5018-a973-4c85ecc9e179" 5 | PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" 6 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 7 | -------------------------------------------------------------------------------- /.github/run-flocking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | 5 | echo "Benchmarking Agents.jl" 6 | julia --project=@. ./Flocking/Agents/benchmark_flocking.jl 7 | 8 | echo "Benchmarking Mason" 9 | bash ./Flocking/Mason/benchmark_flocking.sh 10 | 11 | echo "Benchmarking Mesa" 12 | python3 ./Flocking/Mesa/benchmark_flocking.py 13 | 14 | echo "Benchmarking NetLogo" 15 | bash Flocking/NetLogo/benchmark_flocking.sh 16 | 17 | ) | tee benchmark_results.txt 18 | 19 | julia --project=@. create_benchmark_table.jl 20 | -------------------------------------------------------------------------------- /.github/run-wolfsheep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | 5 | echo "Benchmarking Agents.jl" 6 | julia --project=@. ./WolfSheep/Agents/benchmark_wolfsheep.jl 7 | 8 | echo "Benchmarking Mason" 9 | bash ./WolfSheep/Mason/benchmark_wolfsheep.sh 10 | 11 | echo "Benchmarking Mesa" 12 | python3 ./WolfSheep/Mesa/benchmark_wolfsheep.py 13 | 14 | echo "Benchmarking NetLogo" 15 | bash ./WolfSheep/NetLogo/benchmark_wolfsheep.sh 16 | 17 | ) | tee benchmark_results.txt 18 | 19 | julia --project=@. create_benchmark_table.jl -------------------------------------------------------------------------------- /.github/run-schelling.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | 5 | echo "Benchmarking Agents.jl" 6 | julia --project=@. ./Schelling/Agents/benchmark_schelling.jl 7 | 8 | echo "Benchmarking Mason" 9 | bash ./Schelling/Mason/benchmark_schelling.sh 10 | 11 | echo "Benchmarking Mesa" 12 | python3 ./Schelling/Mesa/benchmark_schelling.py 13 | 14 | echo "Benchmarking NetLogo" 15 | bash Schelling/NetLogo/benchmark_schelling.sh 16 | 17 | ) | tee benchmark_results.txt 18 | 19 | julia --project=@. create_benchmark_table.jl 20 | -------------------------------------------------------------------------------- /.github/run-forestfire.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | 5 | echo "Benchmarking Agents.jl" 6 | julia --project=@. ./ForestFire/Agents/benchmark_forestfire.jl 7 | 8 | echo "Benchmarking Mason" 9 | bash ./ForestFire/Mason/benchmark_forestfire.sh 10 | 11 | echo "Benchmarking Mesa" 12 | python3 ./ForestFire/Mesa/benchmark_forestfire.py 13 | 14 | echo "Benchmarking NetLogo" 15 | bash ./ForestFire/NetLogo/benchmark_forestfire.sh 16 | 17 | ) | tee benchmark_results.txt 18 | 19 | julia --project=@. create_benchmark_table.jl 20 | -------------------------------------------------------------------------------- /seed_netlogo.jl: -------------------------------------------------------------------------------- 1 | using LightXML, Random 2 | 3 | n_run = parse(Int, ARGS[2]) 4 | rng_seed = MersenneTwister(42) 5 | 6 | xdoc = parse_file(ARGS[1]) 7 | xroot = root(xdoc) 8 | 9 | found = false 10 | 11 | for c in child_elements(xroot) 12 | global found 13 | for cc in child_elements(c) 14 | if attribute(cc, "variable"; required=false) == "seed" 15 | for _ in 1:n_run 16 | seed = rand(rng_seed, 1:10000) 17 | e = new_child(cc, "value") 18 | set_attribute(e, "value", seed) 19 | end 20 | found = true 21 | end 22 | end 23 | end 24 | 25 | !found && error("No seed was found in XML!") 26 | save_file(xdoc, ARGS[1]) 27 | free(xdoc) 28 | -------------------------------------------------------------------------------- /runall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | 5 | echo "Benchmarking Agents.jl" 6 | julia --project=@. WolfSheep/Agents/benchmark_wolfsheep.jl 7 | julia --project=@. Flocking/Agents/benchmark_flocking.jl 8 | julia --project=@. Schelling/Agents/benchmark_schelling.jl 9 | 10 | echo "Benchmarking Mason" 11 | bash WolfSheep/Mason/benchmark_wolfsheep.sh 12 | bash Flocking/Mason/benchmark_flocking.sh 13 | bash Schelling/Mason/benchmark_schelling.sh 14 | 15 | echo "Benchmarking Mesa" 16 | python3 WolfSheep/Mesa/benchmark_wolfsheep.py 17 | python3 Flocking/Mesa/benchmark_flocking.py 18 | python3 Schelling/Mesa/benchmark_schelling.py 19 | 20 | echo "Benchmarking NetLogo" 21 | bash WolfSheep/NetLogo/benchmark_wolfsheep.sh 22 | bash Flocking/NetLogo/benchmark_flocking.sh 23 | bash Schelling/NetLogo/benchmark_schelling.sh 24 | 25 | ) | tee benchmark_results.txt 26 | 27 | julia --project=@. create_benchmark_table.jl -------------------------------------------------------------------------------- /Flocking/Agents/benchmark_flocking.jl: -------------------------------------------------------------------------------- 1 | using Agents 2 | using BenchmarkTools 3 | using Random 4 | 5 | include("Flocking.jl") 6 | 7 | rng_seed = MersenneTwister(42) 8 | 9 | rng_model() = Xoshiro(rand(rng_seed, 1:10000)) 10 | 11 | function run_model(rng, extent, n_birds, visual_distance) 12 | model = flocking_model(rng, extent, n_birds, visual_distance) 13 | step!(model, 100) 14 | end 15 | 16 | n_run = 100 17 | 18 | a = @benchmark run_model(rng, (100, 100), 200, 5.0) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 19 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 20 | println("Agents.jl Flocking-small (ms): ", median_time * 1e-6) 21 | 22 | a = @benchmark run_model(rng, (150, 150), 400, 15.0) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 23 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 24 | println("Agents.jl Flocking-large (ms): ", median_time * 1e-6) 25 | -------------------------------------------------------------------------------- /Schelling/Agents/benchmark_schelling.jl: -------------------------------------------------------------------------------- 1 | using Agents 2 | using BenchmarkTools 3 | using Random 4 | 5 | include("Schelling.jl") 6 | 7 | rng_seed = MersenneTwister(42) 8 | 9 | rng_model() = Xoshiro(rand(rng_seed, 1:10000)) 10 | 11 | function run_model(rng, numagents, griddims, min_to_be_happy, radius) 12 | model = schelling_model(rng, numagents, griddims, min_to_be_happy, radius) 13 | step!(model, 20) 14 | end 15 | 16 | n_run = 100 17 | 18 | a = @benchmark run_model(rng, 1000, (40, 40), 3, 1) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 19 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 20 | println("Agents.jl Schelling-small (ms): ", median_time * 1e-6) 21 | 22 | a = @benchmark run_model(rng, 8000, (100, 100), 8, 2) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 23 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 24 | println("Agents.jl Schelling-large (ms): ", median_time * 1e-6) 25 | -------------------------------------------------------------------------------- /.github/setup.sh: -------------------------------------------------------------------------------- 1 | # fetch update software list 2 | sudo apt-get update 3 | 4 | # give permissions 5 | sudo chmod a+rwx ./ 6 | sudo chmod -R 777 ./ 7 | 8 | # install java 9 | sudo apt install default-jre-headless 10 | sudo apt install default-jdk-headless 11 | 12 | # install julia 13 | sudo wget https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.11.5-linux-x86_64.tar.gz 14 | sudo tar zxvf julia-1.11.5-linux-x86_64.tar.gz 15 | export PATH=$PATH:$(pwd)"/julia-1.11.5/bin" 16 | printf "\nexport PATH=\"\$PATH:"$(pwd)"/julia-1.11.5/bin\"" >> ~/.bashrc 17 | 18 | # install agents 19 | julia --project=@. -e 'using Pkg; Pkg.instantiate()' 20 | 21 | # install mesa 22 | sudo apt install python3-pip 23 | pip install mesa==3.2.0 24 | 25 | # install netlogo 26 | sudo wget http://ccl.northwestern.edu/netlogo/6.4.0/NetLogo-6.4.0-64.tgz 27 | sudo tar -xzf NetLogo-6.4.0-64.tgz 28 | sudo mv "NetLogo-6.4.0-64" netlogo 29 | 30 | # install bc tool 31 | sudo apt install bc 32 | -------------------------------------------------------------------------------- /WolfSheep/Agents/benchmark_wolfsheep.jl: -------------------------------------------------------------------------------- 1 | using Agents 2 | using BenchmarkTools 3 | using Random 4 | 5 | include("WolfSheep.jl") 6 | 7 | rng_seed = MersenneTwister(42) 8 | 9 | rng_model() = Xoshiro(rand(rng_seed, 1:10000)) 10 | 11 | function run_model(rng, n_sheep, n_wolves, dims, regrowth_time, 12 | sheep_reproduce, wolf_reproduce) 13 | model = predator_prey_model(rng, n_sheep, n_wolves, dims, regrowth_time, 14 | sheep_reproduce, wolf_reproduce) 15 | step!(model, 100) 16 | end 17 | 18 | n_run = 100 19 | 20 | a = @benchmark run_model(rng, 60, 40, (25, 25), 20, 0.2, 0.1) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 21 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 22 | println("Agents.jl WolfSheep-small (ms): ", median_time * 1e-6) 23 | 24 | a = @benchmark run_model(rng, 1000, 500, (100, 100), 10, 0.4, 0.2) setup=(rng = rng_model()) evals=1 samples=n_run seconds=1e6 25 | median_time = sort(a.times)[n_run ÷ 2 + n_run % 2] 26 | println("Agents.jl WolfSheep-large (ms): ", median_time * 1e-6) 27 | -------------------------------------------------------------------------------- /Schelling/Agents/Schelling.jl: -------------------------------------------------------------------------------- 1 | using Agents 2 | 3 | @agent struct SchellingAgent(GridAgent{2}) 4 | const group::Int # The group of the agent 5 | end 6 | 7 | function schelling_model(rng, numagents, griddims, min_to_be_happy, radius) 8 | space = GridSpaceSingle(griddims, periodic = false) 9 | properties = (min_to_be_happy = min_to_be_happy, radius = radius) 10 | model = StandardABM(SchellingAgent, space; agent_step!, properties, rng, 11 | container = Vector, scheduler = Schedulers.Randomly()) 12 | for n in 1:numagents 13 | add_agent_single!(model, n < numagents / 2 ? 1 : 2) 14 | end 15 | return model 16 | end 17 | 18 | function agent_step!(agent, model) 19 | count_neighbors_same_group = 0 20 | for neighbor in nearby_agents(agent, model, model.radius) 21 | if agent.group == neighbor.group 22 | count_neighbors_same_group += 1 23 | end 24 | end 25 | if count_neighbors_same_group ≥ model.min_to_be_happy 26 | move_agent_single!(agent, model) 27 | end 28 | return 29 | end 30 | -------------------------------------------------------------------------------- /Flocking/Mesa/benchmark_flocking.py: -------------------------------------------------------------------------------- 1 | # Use Python 3 2 | 3 | # Only collect the number of wolves and sheeps per step. 4 | 5 | import timeit 6 | import gc 7 | 8 | setup = """ 9 | gc.enable() 10 | import os, sys 11 | sys.path.insert(0, os.path.abspath(".")) 12 | 13 | from Flocking import BoidFlockers 14 | 15 | import random 16 | random.seed(42) 17 | 18 | def runthemodel(seed, population, width, height, vision): 19 | flock = BoidFlockers(seed, population, width, height, vision) 20 | for i in range(0, 100): 21 | flock.step() 22 | 23 | seed = random.randint(1, 10000) 24 | population={} 25 | width={} 26 | height={} 27 | vision={} 28 | """ 29 | 30 | n_run = 100 31 | 32 | tt = timeit.Timer('runthemodel(seed, population, width, height, vision)', setup=setup.format(200, 100, 100, 5)) 33 | a = tt.repeat(n_run, 1) 34 | median_time = sorted(a)[n_run // 2 + n_run % 2] 35 | print("Mesa Flocking-small (ms):", median_time*1e3) 36 | 37 | tt = timeit.Timer('runthemodel(seed, population, width, height, vision)', setup=setup.format(400, 150, 150, 15)) 38 | a = tt.repeat(n_run, 1) 39 | median_time = sorted(a)[n_run // 2 + n_run % 2] 40 | print("Mesa Flocking-large (ms):", median_time*1e3) 41 | -------------------------------------------------------------------------------- /Schelling/Mesa/benchmark_schelling.py: -------------------------------------------------------------------------------- 1 | # Use Python 3 2 | 3 | import timeit 4 | import gc 5 | 6 | setup = """ 7 | gc.enable() 8 | import os, sys 9 | sys.path.insert(0, os.path.abspath(".")) 10 | 11 | from Schelling import SchellingModel 12 | 13 | import random 14 | random.seed(42) 15 | 16 | def runthemodel(seed, height, width, homophily, radius, density,): 17 | schelling = SchellingModel(seed, height, width, homophily, radius, density,) 18 | for i in range(0, 20): 19 | schelling.step() 20 | 21 | seed = random.randint(1, 10000) 22 | height = {} 23 | width = {} 24 | homophily = {} 25 | radius = {} 26 | density = {} 27 | """ 28 | 29 | n_run = 100 30 | 31 | tt = timeit.Timer('runthemodel(seed, height, width, homophily, radius, density)', 32 | setup=setup.format(40, 40, 3, 1, 0.625)) 33 | a = tt.repeat(n_run, 1) 34 | median_time = sorted(a)[n_run // 2 + n_run % 2] 35 | print("Mesa Schelling-small (ms):", median_time*1e3) 36 | 37 | tt = timeit.Timer('runthemodel(seed, height, width, homophily, radius, density)', 38 | setup=setup.format(100, 100, 8, 2, 0.8)) 39 | a = tt.repeat(n_run, 1) 40 | median_time = sorted(a)[n_run // 2 + n_run % 2] 41 | print("Mesa Schelling-large (ms):", median_time*1e3) 42 | 43 | -------------------------------------------------------------------------------- /Flocking/Mason/benchmark_flocking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SEED=42 4 | RANDOM=$SEED 5 | N_RUN=100 6 | 7 | export CLASSPATH=Flocking/Mason:./mason.22.jar 8 | javac Flocking/Mason/Flocking_small.java Flocking/Mason/Flocker_small.java 9 | javac Flocking/Mason/Flocking_large.java Flocking/Mason/Flocker_large.java 10 | 11 | n_run_model_small () { 12 | times=() 13 | for i in $( seq 1 $N_RUN ) 14 | do 15 | startt=`date +%s%N` 16 | java Flocking_small -seed $((RANDOM % 10000 + 1)) -for 100 -quiet 17 | endt=`date +%s%N` 18 | times+=(`expr $endt - $startt`) 19 | done 20 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 21 | echo -n "Mason Flocking-small (ms): " 22 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 23 | } 24 | 25 | n_run_model_large () { 26 | times=() 27 | for i in $( seq 1 $N_RUN ) 28 | do 29 | startt=`date +%s%N` 30 | java Flocking_large -seed $((RANDOM % 10000 + 1)) -for 100 -quiet 31 | endt=`date +%s%N` 32 | times+=(`expr $endt - $startt`) 33 | done 34 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 35 | echo -n "Mason Flocking-large (ms): " 36 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 37 | } 38 | 39 | n_run_model_small 40 | n_run_model_large 41 | -------------------------------------------------------------------------------- /Schelling/Mason/benchmark_schelling.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SEED=42 4 | RANDOM=$SEED 5 | N_RUN=100 6 | 7 | export CLASSPATH=Schelling/Mason:./mason.22.jar 8 | javac Schelling/Mason/Schelling_small.java Schelling/Mason/Agent_small.java 9 | javac Schelling/Mason/Schelling_large.java Schelling/Mason/Agent_large.java 10 | 11 | n_run_model_small () { 12 | times=() 13 | for i in $( seq 1 $N_RUN ) 14 | do 15 | startt=`date +%s%N` 16 | java Schelling_small -seed $((RANDOM % 10000 + 1)) -for 20 -quiet 17 | endt=`date +%s%N` 18 | times+=(`expr $endt - $startt`) 19 | done 20 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 21 | echo -n "Mason Schelling-small (ms): " 22 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 23 | } 24 | 25 | n_run_model_large () { 26 | times=() 27 | for i in $( seq 1 $N_RUN ) 28 | do 29 | startt=`date +%s%N` 30 | java Schelling_large -seed $((RANDOM % 10000 + 1)) -for 20 -quiet 31 | endt=`date +%s%N` 32 | times+=(`expr $endt - $startt`) 33 | done 34 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 35 | echo -n "Mason Schelling-large (ms): " 36 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 37 | } 38 | 39 | n_run_model_small 40 | n_run_model_large 41 | 42 | -------------------------------------------------------------------------------- /WolfSheep/Mason/benchmark_wolfsheep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SEED=42 4 | RANDOM=$SEED 5 | N_RUN=100 6 | 7 | export CLASSPATH=WolfSheep/Mason:./mason.22.jar 8 | javac WolfSheep/Mason/Wsg_small.java WolfSheep/Mason/Wolf_small.java WolfSheep/Mason/Sheep_small.java 9 | javac WolfSheep/Mason/Wsg_large.java WolfSheep/Mason/Wolf_large.java WolfSheep/Mason/Sheep_large.java 10 | 11 | n_run_model_small () { 12 | times=() 13 | for i in $( seq 1 $N_RUN ) 14 | do 15 | startt=`date +%s%N` 16 | java Wsg_small -seed $((RANDOM % 10000 + 1)) -for 100 -quiet 17 | endt=`date +%s%N` 18 | times+=(`expr $endt - $startt`) 19 | done 20 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 21 | echo -n "Mason WolfSheep-small (ms): " 22 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 23 | } 24 | 25 | n_run_model_large () { 26 | times=() 27 | for i in $( seq 1 $N_RUN ) 28 | do 29 | startt=`date +%s%N` 30 | java Wsg_large -seed $((RANDOM % 10000 + 1)) -for 100 -quiet 31 | endt=`date +%s%N` 32 | times+=(`expr $endt - $startt`) 33 | done 34 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 35 | echo -n "Mason WolfSheep-large (ms): " 36 | echo "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]} * 0.000001" | bc 37 | } 38 | 39 | n_run_model_small 40 | n_run_model_large 41 | -------------------------------------------------------------------------------- /.github/workflows/run-benchmarks.yml: -------------------------------------------------------------------------------- 1 | name: run-benchmarks 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | tags: '*' 10 | 11 | jobs: 12 | Run-benchmark-flocking: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Cancel ongoing test runs for previous commits 16 | uses: styfle/cancel-workflow-action@0.11.0 17 | - uses: actions/checkout@v3 18 | - name: setup-benchmarks 19 | run: bash ./.github/setup.sh 20 | - name: run-benchmarks 21 | run: bash ./.github/run-flocking.sh 22 | Run-benchmark-wolfsheep: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Cancel ongoing test runs for previous commits 26 | uses: styfle/cancel-workflow-action@0.11.0 27 | - uses: actions/checkout@v3 28 | - name: setup-benchmarks 29 | run: bash ./.github/setup.sh 30 | - name: run-benchmarks 31 | run: bash ./.github/run-wolfsheep.sh 32 | Run-benchmark-schelling: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Cancel ongoing test runs for previous commits 36 | uses: styfle/cancel-workflow-action@0.11.0 37 | - uses: actions/checkout@v3 38 | - name: setup-benchmarks 39 | run: bash ./.github/setup.sh 40 | - name: run-benchmarks 41 | run: bash ./.github/run-schelling.sh 42 | -------------------------------------------------------------------------------- /create_benchmark_table.jl: -------------------------------------------------------------------------------- 1 | using PrettyTables 2 | 3 | frameworks = ["Agents.jl", "Mason", "NetLogo", "Mesa"] 4 | 5 | models = ["WolfSheep-small", "WolfSheep-large", 6 | "Flocking-small", "Flocking-large", 7 | "Schelling-small", "Schelling-large", 8 | "ForestFire-small", "ForestFire-large"] 9 | 10 | frameworks_times = Dict(m => Dict(f => 0.0 for f in frameworks) for m in models) 11 | 12 | open("benchmark_results.txt", "r") do f 13 | for line in readlines(f) 14 | !occursin("(ms)", line) && continue 15 | s_line = split(line) 16 | !in(s_line[1], frameworks) && continue 17 | frameworks_times[String(s_line[2])][String(s_line[1])] = parse(Float64, s_line[4]) 18 | end 19 | end 20 | 21 | frameworks_comparison = Dict(m => Dict(f => 0.0 for f in frameworks) for m in models) 22 | for m in models 23 | for f in frameworks 24 | if f == "Agents.jl" 25 | frameworks_comparison[m][f] = 1 26 | else 27 | frameworks_times[m][f] == 0.0 && continue 28 | v = round(frameworks_times[m][f]/frameworks_times[m]["Agents.jl"], digits=1) 29 | frameworks_comparison[m][f] = v 30 | end 31 | end 32 | end 33 | 34 | columns = ["Model/Framework", "Agents.jl 6.2.10", "MASON 22.0", "Netlogo 6.4.0", "Mesa 3.2.0"] 35 | results = mapreduce(permutedims, vcat, [vcat([m], [ifelse(frameworks_comparison[m][f] != 0, frameworks_comparison[m][f], ".") for f in frameworks]) for m in models]) 36 | conf = set_pt_conf(tf = tf_markdown, alignment = :c) 37 | table = pretty_table_with_conf(conf, results; header = columns) 38 | -------------------------------------------------------------------------------- /WolfSheep/Mesa/benchmark_wolfsheep.py: -------------------------------------------------------------------------------- 1 | # Use Python 3 2 | 3 | # Only collect the number of wolves and sheeps per step. 4 | 5 | import timeit 6 | import gc 7 | 8 | setup = """ 9 | gc.enable() 10 | import os, sys 11 | sys.path.insert(0, os.path.abspath(".")) 12 | 13 | from agents import Sheep, Wolf, GrassPatch 14 | from WolfSheep import WolfSheep 15 | 16 | import random 17 | random.seed(42) 18 | 19 | def runthemodel(seed, height, width, initial_sheep, initial_wolves, sheep_reproduce, wolf_reproduce, grass_regrowth_time): 20 | wolfsheep = WolfSheep(seed, height, width, initial_sheep, initial_wolves, sheep_reproduce, wolf_reproduce, grass_regrowth_time) 21 | for i in range(0, 100): 22 | wolfsheep.step() 23 | 24 | seed = random.randint(1, 10000) 25 | height = {} 26 | width = {} 27 | initial_sheep = {} 28 | initial_wolves = {} 29 | sheep_reproduce = {} 30 | wolf_reproduce = {} 31 | grass_regrowth_time = {} 32 | """ 33 | 34 | n_run = 100 35 | 36 | tt = timeit.Timer('runthemodel(seed, height, width, initial_sheep, initial_wolves, sheep_reproduce, wolf_reproduce, grass_regrowth_time)', 37 | setup=setup.format(25, 25, 60, 40, 0.2, 0.1, 20)) 38 | a = tt.repeat(n_run, 1) 39 | median_time = sorted(a)[n_run // 2 + n_run % 2] 40 | print("Mesa WolfSheep-small (ms):", median_time*1e3) 41 | 42 | tt = timeit.Timer('runthemodel(seed, height, width, initial_sheep, initial_wolves, sheep_reproduce, wolf_reproduce, grass_regrowth_time)', 43 | setup=setup.format(100, 100, 1000, 500, 0.4, 0.2, 10)) 44 | a = tt.repeat(n_run, 1) 45 | median_time = sorted(a)[n_run // 2 + n_run % 2] 46 | print("Mesa WolfSheep-large (ms):", median_time*1e3) -------------------------------------------------------------------------------- /Flocking/NetLogo/benchmark_flocking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NetLogo's profiler sucks in the sense that it times one run, then spits out a bunch of junk 4 | # to either a file or stdout. There's no easy abilitiy to parse it. 5 | 6 | # the netlogo folder is assumed to be inside the NetLogo folder of this repository, which contains the models 7 | 8 | N_RUN=100 9 | 10 | NAME_LAUNCHER="./netlogo/netlogo-headless.sh" 11 | NAME_MODEL="Flocking/NetLogo/Flocking.nlogo" 12 | NAME_PARAM="Flocking/NetLogo/parameters_flocking.xml" 13 | NAME_TIMES="Flocking/NetLogo/times.txt" 14 | 15 | julia --project=@. seed_netlogo.jl $NAME_PARAM $N_RUN 16 | 17 | n_run_model_small () { 18 | sed -i '1d' $NAME_PARAM 19 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_small \ 20 | --min-pxcor 0 --max-pxcor 99 --min-pycor 0 --max-pycor 99 --threads 1) 21 | times=() 22 | while IFS= read -r line; do 23 | times+=(`expr $line`) 24 | done < $NAME_TIMES 25 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 26 | printf "NetLogo Flocking-small (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 27 | rm $NAME_TIMES 28 | } 29 | 30 | n_run_model_large () { 31 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_large \ 32 | --min-pxcor 0 --max-pxcor 149 --min-pycor 0 --max-pycor 149 --threads 1) 33 | times=() 34 | while IFS= read -r line; do 35 | times+=(`expr $line`) 36 | done < $NAME_TIMES 37 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 38 | printf "NetLogo Flocking-large (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 39 | rm $NAME_TIMES 40 | } 41 | 42 | n_run_model_small 43 | n_run_model_large 44 | -------------------------------------------------------------------------------- /Schelling/NetLogo/benchmark_schelling.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NetLogo's profiler sucks in the sense that it times one run, then spits out a bunch of junk 4 | # to either a file or stdout. There's no easy abilitiy to parse it. 5 | 6 | # the netlogo folder is assumed to be inside the NetLogo folder of this repository, which contains the models 7 | 8 | N_RUN=100 9 | 10 | NAME_LAUNCHER="./netlogo/netlogo-headless.sh" 11 | NAME_MODEL="Schelling/NetLogo/Schelling.nlogo" 12 | NAME_PARAM="Schelling/NetLogo/parameters_schelling.xml" 13 | NAME_TIMES="Schelling/NetLogo/times.txt" 14 | 15 | julia --project=@. seed_netlogo.jl $NAME_PARAM $N_RUN 16 | 17 | n_run_model_small () { 18 | sed -i '1d' $NAME_PARAM 19 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_small \ 20 | --min-pxcor 0 --max-pxcor 39 --min-pycor 0 --max-pycor 39 --threads 1) 21 | times=() 22 | while IFS= read -r line; do 23 | times+=(`expr $line`) 24 | done < $NAME_TIMES 25 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 26 | printf "NetLogo Schelling-small (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 27 | rm $NAME_TIMES 28 | } 29 | 30 | n_run_model_large () { 31 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_large \ 32 | --min-pxcor 0 --max-pxcor 99 --min-pycor 0 --max-pycor 99 --threads 1) 33 | times=() 34 | while IFS= read -r line; do 35 | times+=(`expr $line`) 36 | done < $NAME_TIMES 37 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 38 | printf "NetLogo Schelling-large (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 39 | rm $NAME_TIMES 40 | } 41 | 42 | n_run_model_small 43 | n_run_model_large 44 | -------------------------------------------------------------------------------- /Flocking/Agents/Flocking.jl: -------------------------------------------------------------------------------- 1 | using Agents, LinearAlgebra 2 | 3 | @agent struct Bird(ContinuousAgent{2, Float64}) 4 | const speed::Float64 5 | const cohere_factor::Float64 6 | const separation::Float64 7 | const separate_factor::Float64 8 | const match_factor::Float64 9 | const visual_distance::Float64 10 | end 11 | 12 | function flocking_model(rng, extent, n_birds, visual_distance; 13 | speed = 1.0, cohere_factor = 0.03, separation = 1.0, separate_factor = 0.015, 14 | match_factor = 0.05, spacing = visual_distance / 1.5,) 15 | space2d = ContinuousSpace(extent; spacing) 16 | model = StandardABM(Bird, space2d; agent_step!, rng, container = Vector, 17 | scheduler = Schedulers.Randomly()) 18 | for n in 1:n_birds 19 | vel = SVector{2}(rand(abmrng(model)) * 2 - 1 for _ in 1:2) 20 | add_agent!(model, vel, speed, cohere_factor, separation, 21 | separate_factor, match_factor, visual_distance) 22 | end 23 | return model 24 | end 25 | 26 | function agent_step!(bird, model) 27 | neighbor_agents = nearby_agents(bird, model, bird.visual_distance) 28 | N = 0 29 | match = separate = cohere = SVector{2}(0.0, 0.0) 30 | for neighbor in neighbor_agents 31 | N += 1 32 | heading = get_direction(bird.pos, neighbor.pos, model) 33 | cohere += heading 34 | match += neighbor.vel 35 | if sum(heading .^ 2) < bird.separation^2 36 | separate -= heading 37 | end 38 | end 39 | cohere *= bird.cohere_factor 40 | separate *= bird.separate_factor 41 | match *= bird.match_factor 42 | bird.vel += (cohere + separate + match) / max(N, 1) 43 | bird.vel /= norm(bird.vel) 44 | move_agent!(bird, model, bird.speed) 45 | end 46 | -------------------------------------------------------------------------------- /WolfSheep/NetLogo/benchmark_wolfsheep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NetLogo's profiler sucks in the sense that it times one run, then spits out a bunch of junk 4 | # to either a file or stdout. There's no easy abilitiy to parse it. 5 | 6 | # the netlogo folder is assumed to be inside the NetLogo folder of this repository, which contains the models 7 | 8 | N_RUN=100 9 | 10 | NAME_LAUNCHER="./netlogo/netlogo-headless.sh" 11 | NAME_MODEL="WolfSheep/NetLogo/WolfSheep.nlogo" 12 | NAME_PARAM="WolfSheep/NetLogo/parameters_wolfsheep.xml" 13 | NAME_TIMES="WolfSheep/NetLogo/times.txt" 14 | 15 | julia --project=@. seed_netlogo.jl $NAME_PARAM $N_RUN 16 | 17 | n_run_model_small () { 18 | sed -i '1d' $NAME_PARAM 19 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_small \ 20 | --min-pxcor 0 --max-pxcor 24 --min-pycor 0 --max-pycor 24 --threads 1) 21 | times=() 22 | while IFS= read -r line; do 23 | times+=(`expr $line`) 24 | done < $NAME_TIMES 25 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 26 | printf "NetLogo WolfSheep-small (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 27 | rm $NAME_TIMES 28 | } 29 | 30 | n_run_model_large () { 31 | (bash $NAME_LAUNCHER --model $NAME_MODEL --setup-file $NAME_PARAM --experiment benchmark_large \ 32 | --min-pxcor 0 --max-pxcor 99 --min-pycor 0 --max-pycor 99 --threads 1) 33 | times=() 34 | while IFS= read -r line; do 35 | times+=(`expr $line`) 36 | done < $NAME_TIMES 37 | readarray -t sorted < <(printf '%s\n' "${times[@]}" | sort) 38 | printf "NetLogo WolfSheep-large (ms): "${sorted[(`expr $N_RUN / 2 + $N_RUN % 2`)]}"\n" 39 | rm $NAME_TIMES 40 | } 41 | 42 | n_run_model_small 43 | n_run_model_large 44 | 45 | -------------------------------------------------------------------------------- /Schelling/NetLogo/parameters_schelling.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | setup 4 | benchmark 5 | ticks = 20 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | setup 26 | benchmark 27 | ticks = 20 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Schelling/Mason/Agent_large.java: -------------------------------------------------------------------------------- 1 | import sim.field.grid.ObjectGrid2D; 2 | import sim.engine.SimState; 3 | import sim.util.IntBag; 4 | import sim.util.Int2D; 5 | import sim.engine.Steppable; 6 | 7 | public class Agent_large implements Steppable 8 | { 9 | Int2D loc; 10 | public ObjectGrid2D neighbors; 11 | public int group; 12 | IntBag neighborsX; 13 | IntBag neighborsY; 14 | 15 | public Agent_large(final int x, final int y, final int group) { 16 | this.loc = new Int2D(x, y); 17 | this.group = group; 18 | this.neighborsX = new IntBag(25); 19 | this.neighborsY = new IntBag(25); 20 | } 21 | 22 | public void step(final SimState state) { 23 | final Schelling_large sch = (Schelling_large)state; 24 | final int x = this.loc.x; 25 | final int y = this.loc.y; 26 | final int neighborhood = sch.neighborhood; 27 | final ObjectGrid2D neighbors2 = sch.neighbors; 28 | neighbors2.getMooreLocations(x, y, neighborhood, 0, true, this.neighborsX, this.neighborsY); 29 | double val = 0.0; 30 | final int threshold = sch.threshold; 31 | final int numObjs = this.neighborsX.numObjs; 32 | final int[] objsX = this.neighborsX.objs; 33 | final int[] objsY = this.neighborsY.objs; 34 | for (int i = 0; i < numObjs; ++i) { 35 | Object o = neighbors2.field[objsX[i]][objsY[i]]; 36 | if (o != null) { 37 | Agent_large neigh = (Agent_large)o; 38 | if (neigh.group == this.group && (objsX[i] != x || objsY[i] != y)) { 39 | val += 1.0; 40 | } 41 | } 42 | } 43 | if (val < threshold) { 44 | final int newLocIndex = state.random.nextInt(sch.emptySpaces.numObjs); 45 | final Int2D newLoc = (Int2D)sch.emptySpaces.objs[newLocIndex]; 46 | sch.emptySpaces.objs[newLocIndex] = this.loc; 47 | this.loc = newLoc; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Schelling/Mason/Agent_small.java: -------------------------------------------------------------------------------- 1 | import sim.field.grid.ObjectGrid2D; 2 | import sim.engine.SimState; 3 | import sim.util.IntBag; 4 | import sim.util.Int2D; 5 | import sim.engine.Steppable; 6 | 7 | public class Agent_small implements Steppable 8 | { 9 | Int2D loc; 10 | public ObjectGrid2D neighbors; 11 | public int group; 12 | IntBag neighborsX; 13 | IntBag neighborsY; 14 | 15 | public Agent_small(final int x, final int y, final int group) { 16 | this.loc = new Int2D(x, y); 17 | this.group = group; 18 | this.neighborsX = new IntBag(9); 19 | this.neighborsY = new IntBag(9); 20 | } 21 | 22 | public void step(final SimState state) { 23 | final Schelling_small sch = (Schelling_small)state; 24 | final int x = this.loc.x; 25 | final int y = this.loc.y; 26 | final int neighborhood = sch.neighborhood; 27 | final ObjectGrid2D neighbors2 = sch.neighbors; 28 | neighbors2.getMooreLocations(x, y, neighborhood, 0, true, this.neighborsX, this.neighborsY); 29 | double val = 0.0; 30 | final int threshold = sch.threshold; 31 | final int numObjs = this.neighborsX.numObjs; 32 | final int[] objsX = this.neighborsX.objs; 33 | final int[] objsY = this.neighborsY.objs; 34 | for (int i = 0; i < numObjs; ++i) { 35 | Object o = neighbors2.field[objsX[i]][objsY[i]]; 36 | if (o != null) { 37 | Agent_small neigh = (Agent_small)o; 38 | if (neigh.group == this.group && (objsX[i] != x || objsY[i] != y)) { 39 | val += 1.0; 40 | } 41 | } 42 | } 43 | 44 | if (val < threshold) { 45 | final int newLocIndex = state.random.nextInt(sch.emptySpaces.numObjs); 46 | final Int2D newLoc = (Int2D)sch.emptySpaces.objs[newLocIndex]; 47 | sch.emptySpaces.objs[newLocIndex] = this.loc; 48 | this.loc = newLoc; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Flocking/Mason/Flocking_large.java: -------------------------------------------------------------------------------- 1 | import sim.engine.Steppable; 2 | import sim.util.Bag; 3 | import sim.util.Double2D; 4 | import sim.field.continuous.Continuous2D; 5 | import sim.engine.SimState; 6 | import sim.engine.RandomSequence; 7 | 8 | public class Flocking_large extends SimState 9 | { 10 | public Continuous2D flockers; 11 | public double width; 12 | public double height; 13 | public int numFlockers; 14 | public double cohesion; 15 | public double avoidance; 16 | public double consistency; 17 | public double momentum; 18 | public double neighborhood; 19 | 20 | public Flocking_large(final long seed) { 21 | super(seed); 22 | this.width = 150.0; 23 | this.height = 150.0; 24 | this.numFlockers = 400; 25 | this.neighborhood = 15.0; 26 | this.cohesion = 0.03; 27 | this.avoidance = 0.015; 28 | this.consistency = 0.05; 29 | this.momentum = 1.0; 30 | } 31 | 32 | public void start() { 33 | super.start(); 34 | Steppable[] array_agents = new Steppable[numFlockers]; 35 | int count = 0; 36 | this.flockers = new Continuous2D(this.neighborhood / 1.5, this.width, this.height); 37 | for (int x = 0; x < this.numFlockers; ++x) { 38 | final Double2D location = new Double2D(this.random.nextDouble() * this.width, this.random.nextDouble() * this.height); 39 | final Double2D vel = new Double2D(this.random.nextDouble()*2-1, this.random.nextDouble()*2-1); 40 | final Flocker_large flocker = new Flocker_large(location, vel); 41 | this.flockers.setObjectLocation((Object)flocker, location); 42 | flocker.flockers = this.flockers; 43 | flocker.theFlock = this; 44 | array_agents[count] = flocker; 45 | ++count; 46 | } 47 | this.schedule.scheduleRepeating(new RandomSequence(array_agents)); 48 | } 49 | 50 | public static void main(final String[] args) { 51 | doLoop((Class)Flocking_large.class, args); 52 | System.exit(0); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Flocking/Mason/Flocking_small.java: -------------------------------------------------------------------------------- 1 | import sim.engine.Steppable; 2 | import sim.util.Bag; 3 | import sim.util.Double2D; 4 | import sim.field.continuous.Continuous2D; 5 | import sim.engine.SimState; 6 | import sim.engine.RandomSequence; 7 | 8 | public class Flocking_small extends SimState 9 | { 10 | public Continuous2D flockers; 11 | public double width; 12 | public double height; 13 | public int numFlockers; 14 | public double cohesion; 15 | public double avoidance; 16 | public double consistency; 17 | public double momentum; 18 | public double neighborhood; 19 | 20 | public Flocking_small(final long seed) { 21 | super(seed); 22 | this.width = 100.0; 23 | this.height = 100.0; 24 | this.numFlockers = 200; 25 | this.neighborhood = 5.0; 26 | this.cohesion = 0.03; 27 | this.avoidance = 0.015; 28 | this.consistency = 0.05; 29 | this.momentum = 1.0; 30 | } 31 | 32 | public void start() { 33 | super.start(); 34 | Steppable[] array_agents = new Steppable[numFlockers]; 35 | int count = 0; 36 | this.flockers = new Continuous2D(this.neighborhood / 1.5, this.width, this.height); 37 | for (int x = 0; x < this.numFlockers; ++x) { 38 | final Double2D location = new Double2D(this.random.nextDouble() * this.width, this.random.nextDouble() * this.height); 39 | final Double2D vel = new Double2D(this.random.nextDouble()*2-1, this.random.nextDouble()*2-1); 40 | final Flocker_small flocker = new Flocker_small(location, vel); 41 | this.flockers.setObjectLocation((Object)flocker, location); 42 | flocker.flockers = this.flockers; 43 | flocker.theFlock = this; 44 | array_agents[count] = flocker; 45 | ++count; 46 | } 47 | this.schedule.scheduleRepeating(new RandomSequence(array_agents)); 48 | } 49 | 50 | public static void main(final String[] args) { 51 | doLoop((Class)Flocking_small.class, args); 52 | System.exit(0); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Schelling/Mesa/Schelling.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import mesa 4 | from mesa import Model 5 | from mesa.discrete_space import CellAgent, OrthogonalMooreGrid 6 | 7 | class SchellingAgent(CellAgent): 8 | ''' 9 | Schelling segregation agent 10 | ''' 11 | def __init__(self, model, cell, agent_type): 12 | ''' 13 | Create a new Schelling agent. 14 | Args: 15 | unique_id: Unique identifier for the agent. 16 | x, y: Agent initial location. 17 | agent_type: Indicator for the agent's type (minority=1, majority=0) 18 | ''' 19 | super().__init__(model) 20 | self.cell = cell 21 | self.type = agent_type 22 | 23 | def step(self): 24 | similar = 0 25 | r = self.model.radius 26 | for neighbor in self.cell.get_neighborhood(radius=self.model.radius).agents: 27 | if neighbor.type == self.type: 28 | similar += 1 29 | 30 | # If unhappy, move: 31 | if similar < self.model.homophily: 32 | self.cell = self.model.grid.select_random_empty_cell() 33 | 34 | class SchellingModel(Model): 35 | ''' 36 | Model class for the Schelling segregation model. 37 | ''' 38 | 39 | def __init__(self, seed, height, width, homophily, radius, density, minority_pc=0.5): 40 | ''' 41 | ''' 42 | super().__init__(seed=seed) 43 | self.height = height 44 | self.width = width 45 | self.density = density 46 | self.minority_pc = minority_pc 47 | self.homophily = homophily 48 | self.radius = radius 49 | self.grid = OrthogonalMooreGrid((width, height), random=self.random, capacity=1) 50 | 51 | for cell in self.grid.all_cells: 52 | if self.random.random() < self.density: 53 | agent_type = 1 if self.random.random() < minority_pc else 0 54 | SchellingAgent(self, cell, agent_type) 55 | 56 | def step(self): 57 | ''' 58 | Run one step of the model. 59 | ''' 60 | self.agents.shuffle_do("step") 61 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Agents.jl package is licensed under the MIT "Expat" License: 2 | 3 | > The MIT License (MIT) 4 | > 5 | > Copyright (c) 2019-present, George Datseris, Tim DuBois, Ali R. Vahdati, Aayush Sabharwal, Adriano Meligrana and other open source contributors 6 | > 7 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | > 9 | >The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | > 11 | >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A >PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | 14 | This repository includes code for benchmarking purposes from the following projects, which have their own licenses: 15 | - [Mesa](https://github.com/projectmesa/mesa/blob/master/LICENSE) [Apache2] 16 | - [MASON](https://github.com/eclab/mason/blob/master/LICENSE) [AFL] 17 | - [NetLogo, Flocking](http://ccl.northwestern.edu/netlogo/models/Flocking) [Creative Commons Attribution-NonCommercial-ShareAlike 3.0] 18 | - [NetLogo, Wolf Sheep Predation](http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation) [Creative Commons Attribution-NonCommercial-ShareAlike 3.0] 19 | - [NetLogo, Fire](http://ccl.northwestern.edu/netlogo/models/Fire) [Creative Commons Attribution-NonCommercial-ShareAlike 3.0] 20 | - [NetLogo, Segregation](http://ccl.northwestern.edu/netlogo/models/Segregation) [Creative Commons Attribution-NonCommercial-ShareAlike 3.0] 21 | -------------------------------------------------------------------------------- /Flocking/NetLogo/parameters_flocking.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | setup 4 | benchmark 5 | ticks = 100 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | setup 32 | benchmark 33 | ticks = 100 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /WolfSheep/Mason/Sheep_large.java: -------------------------------------------------------------------------------- 1 | import sim.engine.*; 2 | import sim.field.continuous.*; 3 | import sim.field.grid.IntGrid2D; 4 | import sim.field.grid.SparseGrid2D; 5 | import sim.util.*; 6 | 7 | public class Sheep_large implements Steppable 8 | { 9 | public Int2D loc = new Int2D(0,0); 10 | public SparseGrid2D fieldSheeps; 11 | public IntGrid2D fieldGrass; 12 | public Wsg_large theWsg; 13 | public boolean dead = false; 14 | public int energy = 0; 15 | public Sheep_large(Int2D location) { loc = location; } 16 | 17 | public boolean isDead() { return dead; } 18 | public void setDead(boolean val) { dead = val; } 19 | 20 | public void step(SimState state) 21 | { 22 | if (dead) return; 23 | 24 | final Wsg_large wsg = (Wsg_large)state; 25 | 26 | boolean moved = false; 27 | int x = loc.x; 28 | int y = loc.y; 29 | 30 | int xmin = (x>0) ? -1 : 0; 31 | int xmax = (x0) ? -1 : 0; 33 | int ymax = (y0) ? -1 : 0; 31 | int xmax = (x0) ? -1 : 0; 33 | int ymax = (y0) ? -1 : 0; 29 | int xmax = (x0) ? -1 : 0; 31 | int ymax = (y 0){ 42 | for (int k = 0; k < a.numObjs; k++) { 43 | Sheep_large s = (Sheep_large) a.objs[k]; 44 | if (!s.isDead()) 45 | { 46 | b.add(s); 47 | } 48 | } 49 | } 50 | 51 | if (b != null && b.numObjs > 0){ 52 | int q = wsg.random.nextInt(b.numObjs); 53 | Sheep_large s = (Sheep_large) b.objs[q]; 54 | s.setDead(true); 55 | energy += 13; 56 | } 57 | 58 | energy -= 1; 59 | 60 | if (energy <= 0) { 61 | dead = true; 62 | state.schedule.scheduleOnce(this); 63 | return; 64 | } 65 | if (wsg.random.nextDouble() < 0.2) { 66 | energy /= 2; 67 | Int2D location = new Int2D(loc.x, loc.y); 68 | 69 | Wolf_large w = new Wolf_large(location); 70 | w.energy = energy; 71 | 72 | wsg.fieldWolves.setObjectLocation(w, location); 73 | Schedule schedule = state.schedule; 74 | schedule.scheduleRepeating(w, 2, 1.0); 75 | } 76 | 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /WolfSheep/Agents/WolfSheep.jl: -------------------------------------------------------------------------------- 1 | using Agents 2 | 3 | @agent struct Wolf(GridAgent{2}) 4 | energy::Float64 5 | const reproduction_prob::Float64 6 | const Δenergy::Float64 7 | end 8 | 9 | @agent struct Sheep(GridAgent{2}) 10 | energy::Float64 11 | const reproduction_prob::Float64 12 | const Δenergy::Float64 13 | end 14 | 15 | function predator_prey_model(rng, n_sheep, n_wolves, dims, 16 | regrowth_time, sheep_reproduce, wolf_reproduce; 17 | Δenergy_sheep = 5, Δenergy_wolf = 13,) 18 | space = GridSpace(dims, periodic = false) 19 | properties = (fully_grown = falses(dims), countdown = zeros(Int, dims), 20 | regrowth_time = regrowth_time) 21 | scheduler = Schedulers.ByType(true, true, Union{Wolf, Sheep}) 22 | model = ABM(Union{Wolf, Sheep}, space; agent_step!, model_step!, scheduler, 23 | properties, rng, warn = false) 24 | for _ in 1:n_sheep 25 | energy = rand(abmrng(model), 0:(Δenergy_sheep * 2 - 1)) 26 | add_agent!(Sheep, model, energy, sheep_reproduce, Δenergy_sheep) 27 | end 28 | for _ in 1:n_wolves 29 | energy = rand(abmrng(model), 0:(Δenergy_wolf * 2 - 1)) 30 | add_agent!(Wolf, model, energy, wolf_reproduce, Δenergy_wolf) 31 | end 32 | @inbounds for p in positions(model) 33 | fully_grown = rand(abmrng(model), Bool) 34 | countdown = fully_grown ? regrowth_time : rand(abmrng(model), 0:(regrowth_time - 1)) 35 | model.countdown[p...] = countdown 36 | model.fully_grown[p...] = fully_grown 37 | end 38 | return model 39 | end 40 | 41 | function agent_step!(agent, model) 42 | randomwalk!(agent, model; ifempty = false) 43 | agent.energy -= 1 44 | eat!(agent, model) 45 | if agent.energy < 0 46 | remove_agent!(agent, model) 47 | elseif rand(abmrng(model)) <= agent.reproduction_prob 48 | agent.energy /= 2 49 | replicate!(agent, model) 50 | end 51 | end 52 | 53 | function eat!(sheep::Sheep, model) 54 | if model.fully_grown[sheep.pos...] 55 | sheep.energy += sheep.Δenergy 56 | model.fully_grown[sheep.pos...] = false 57 | end 58 | end 59 | 60 | function eat!(wolf::Wolf, model) 61 | dinner = random_agent_in_position(wolf.pos, model, is_sheep) 62 | if !isnothing(dinner) 63 | remove_agent!(dinner, model) 64 | wolf.energy += wolf.Δenergy 65 | end 66 | end 67 | 68 | is_sheep(agent) = agent isa Sheep 69 | 70 | function model_step!(model) 71 | @inbounds for p in positions(model) 72 | if !(model.fully_grown[p...]) 73 | if model.countdown[p...] ≤ 0 74 | model.fully_grown[p...] = true 75 | model.countdown[p...] = model.regrowth_time 76 | else 77 | model.countdown[p...] -= 1 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /WolfSheep/Mason/Wolf_small.java: -------------------------------------------------------------------------------- 1 | import sim.engine.*; 2 | import sim.field.continuous.*; 3 | import sim.field.grid.IntGrid2D; 4 | import sim.field.grid.SparseGrid2D; 5 | import sim.util.*; 6 | 7 | public class Wolf_small implements Steppable 8 | { 9 | public Int2D loc = new Int2D(0,0); 10 | public Int2D lastd = new Int2D(0,0); 11 | public SparseGrid2D fieldWolves; 12 | public SparseGrid2D fieldSheeps; 13 | public Wsg_small theWsg; 14 | public boolean dead = false; 15 | public int energy = 0; 16 | public Wolf_small(Int2D location) { loc = location; } 17 | 18 | public boolean isDead() { return dead; } 19 | public void setDead(boolean val) { dead = val; } 20 | 21 | public void step(SimState state) 22 | { 23 | if (dead) return; 24 | 25 | final Wsg_small wsg = (Wsg_small)state; 26 | 27 | int x = loc.x; 28 | int y = loc.y; 29 | 30 | int xmin = (x>0) ? -1 : 0; 31 | int xmax = (x0) ? -1 : 0; 33 | int ymax = (y 0){ 46 | for (int k = 0; k < a.numObjs; k++) { 47 | Sheep_small s = (Sheep_small) a.objs[k]; 48 | if (!s.isDead()) 49 | { 50 | b.add(s); 51 | } 52 | } 53 | } 54 | if (b != null && b.numObjs > 0){ 55 | int q = wsg.random.nextInt(b.numObjs); 56 | Sheep_small s = (Sheep_small) b.objs[q]; 57 | s.setDead(true); 58 | energy += 13; 59 | } 60 | 61 | energy -= 1; 62 | if (energy <= 0) { 63 | dead = true; 64 | state.schedule.scheduleOnce(this); 65 | return; 66 | } 67 | 68 | //reproduce 69 | if (wsg.random.nextDouble() < 0.1) { 70 | energy /= 2; 71 | Int2D location = new Int2D(loc.x, loc.y); 72 | 73 | Wolf_small w = new Wolf_small(location); 74 | w.energy = energy; 75 | 76 | wsg.fieldWolves.setObjectLocation(w, location); 77 | Schedule schedule = state.schedule; 78 | schedule.scheduleRepeating(w, 2, 1.0); 79 | } 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /Schelling/Mason/Schelling_small.java: -------------------------------------------------------------------------------- 1 | import sim.engine.Steppable; 2 | import sim.util.Int2D; 3 | import sim.util.Bag; 4 | import sim.field.grid.ObjectGrid2D; 5 | import sim.engine.SimState; 6 | import sim.engine.RandomSequence; 7 | 8 | public class Schelling_small extends SimState 9 | { 10 | public int gridHeight; 11 | public int gridWidth; 12 | public int neighborhood; 13 | public int threshold; 14 | public double redProbability; 15 | public double blueProbability; 16 | public double emptyProbability; 17 | public double unavailableProbability; 18 | public ObjectGrid2D neighbors; 19 | public Bag emptySpaces; 20 | public Bag fillSpaces; 21 | 22 | public Schelling_small(final long seed) { 23 | this(seed, 40, 40); 24 | } 25 | 26 | public Schelling_small(final long seed, final int width, final int height) { 27 | super(seed); 28 | this.neighborhood = 1; 29 | this.threshold = 3; 30 | this.redProbability = 0.3125; 31 | this.blueProbability = 0.3125; 32 | this.emptyProbability = 0.375; 33 | this.emptySpaces = new Bag(); 34 | this.fillSpaces = new Bag(); 35 | this.gridWidth = width; 36 | this.gridHeight = height; 37 | } 38 | 39 | public void start() { 40 | super.start(); 41 | for (int x = 0; x < this.gridWidth; ++x) { 42 | for (int y = 0; y < this.gridHeight; ++y) { 43 | final double d = this.random.nextDouble(); 44 | if (d < this.redProbability + this.blueProbability) { 45 | this.fillSpaces.add((Object)new Int2D(x, y)); 46 | } 47 | else { 48 | this.emptySpaces.add((Object)new Int2D(x, y)); 49 | } 50 | } 51 | } 52 | 53 | this.neighbors = new ObjectGrid2D(this.gridWidth, this.gridHeight); 54 | 55 | Steppable[] array_agents = new Steppable[this.fillSpaces.numObjs]; 56 | int count = 0; 57 | 58 | for (int i = 0; i < this.fillSpaces.numObjs; ++i) { 59 | double d = this.random.nextDouble(); 60 | Int2D pos = (Int2D)this.fillSpaces.objs[i]; 61 | if (d < 0.5) { 62 | Agent_small agent = new Agent_small(pos.x, pos.y, 1); 63 | this.neighbors.field[pos.x][pos.y] = agent; 64 | array_agents[count] = agent; 65 | } 66 | else { 67 | Agent_small agent = new Agent_small(pos.x, pos.y, 2); 68 | this.neighbors.field[pos.x][pos.y] = agent; 69 | array_agents[count] = agent; 70 | } 71 | ++count; 72 | } 73 | 74 | this.schedule.scheduleRepeating(new RandomSequence(array_agents)); 75 | } 76 | 77 | public static void main(final String[] args) { 78 | doLoop((Class)Schelling_small.class, args); 79 | System.exit(0); 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Schelling/Mason/Schelling_large.java: -------------------------------------------------------------------------------- 1 | import sim.engine.Steppable; 2 | import sim.util.Int2D; 3 | import sim.util.Bag; 4 | import sim.field.grid.ObjectGrid2D; 5 | import sim.engine.SimState; 6 | import sim.engine.RandomSequence; 7 | 8 | public class Schelling_large extends SimState 9 | { 10 | public int gridHeight; 11 | public int gridWidth; 12 | public int neighborhood; 13 | public int threshold; 14 | public double redProbability; 15 | public double blueProbability; 16 | public double emptyProbability; 17 | public double unavailableProbability; 18 | public ObjectGrid2D neighbors; 19 | public Bag emptySpaces; 20 | public Bag fillSpaces; 21 | 22 | public Schelling_large(final long seed) { 23 | this(seed, 100, 100); 24 | } 25 | 26 | public Schelling_large(final long seed, final int width, final int height) { 27 | super(seed); 28 | this.neighborhood = 2; 29 | this.threshold = 8; 30 | this.redProbability = 0.4; 31 | this.blueProbability = 0.4; 32 | this.emptyProbability = 0.2; 33 | this.emptySpaces = new Bag(); 34 | this.fillSpaces = new Bag(); 35 | this.gridWidth = width; 36 | this.gridHeight = height; 37 | } 38 | 39 | public void start() { 40 | super.start(); 41 | for (int x = 0; x < this.gridWidth; ++x) { 42 | for (int y = 0; y < this.gridHeight; ++y) { 43 | final double d = this.random.nextDouble(); 44 | if (d < this.redProbability + this.blueProbability) { 45 | this.fillSpaces.add((Object)new Int2D(x, y)); 46 | } 47 | else { 48 | this.emptySpaces.add((Object)new Int2D(x, y)); 49 | } 50 | } 51 | } 52 | 53 | this.neighbors = new ObjectGrid2D(this.gridWidth, this.gridHeight); 54 | 55 | Steppable[] array_agents = new Steppable[this.fillSpaces.numObjs]; 56 | int count = 0; 57 | 58 | for (int i = 0; i < this.fillSpaces.numObjs; ++i) { 59 | double d = this.random.nextDouble(); 60 | Int2D pos = (Int2D)this.fillSpaces.objs[i]; 61 | if (d < 0.5) { 62 | Agent_large agent = new Agent_large(pos.x, pos.y, 1); 63 | this.neighbors.field[pos.x][pos.y] = agent; 64 | array_agents[count] = agent; 65 | } 66 | else { 67 | Agent_large agent = new Agent_large(pos.x, pos.y, 2); 68 | this.neighbors.field[pos.x][pos.y] = agent; 69 | array_agents[count] = agent; 70 | } 71 | ++count; 72 | } 73 | 74 | this.schedule.scheduleRepeating(new RandomSequence(array_agents)); 75 | } 76 | 77 | public static void main(final String[] args) { 78 | doLoop((Class)Schelling_large.class, args); 79 | System.exit(0); 80 | } 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /WolfSheep/NetLogo/parameters_wolfsheep.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | setup 4 | benchmark 5 | ticks = 100 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | setup 41 | benchmark 42 | ticks = 100 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Flocking/Mesa/Flocking.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flockers 3 | ============================================================= 4 | A Mesa implementation of Craig Reynolds's Boids flocker model. 5 | Uses numpy arrays to represent vectors. 6 | """ 7 | 8 | import numpy as np 9 | 10 | from mesa import Model 11 | from mesa.experimental.continuous_space import ContinuousSpace 12 | 13 | from boid import Boid 14 | 15 | class BoidFlockers(Model): 16 | """ 17 | Flocker model class. Handles agent creation, placement and scheduling. 18 | """ 19 | 20 | def __init__( 21 | self, 22 | seed, 23 | population_size, 24 | width, 25 | height, 26 | vision, 27 | speed=1, 28 | separation=1, 29 | cohere=0.03, 30 | separate=0.015, 31 | match=0.05, 32 | ): 33 | """ 34 | Create a new Flockers model. 35 | 36 | Args: 37 | population: Number of Boids 38 | width, height: Size of the space. 39 | speed: How fast should the Boids move. 40 | vision: How far around should each Boid look for its neighbors 41 | separation: What's the minimum distance each Boid will attempt to 42 | keep from any other 43 | cohere, separate, match: factors for the relative importance of 44 | the three drives. """ 45 | super().__init__(seed=seed) 46 | 47 | # Set up the space 48 | self.space = ContinuousSpace( 49 | [[0, width], [0, height]], 50 | torus=True, 51 | random=self.random, 52 | n_agents=population_size, 53 | ) 54 | 55 | # Create and place the Boid agents 56 | positions = self.rng.random(size=(population_size, 2)) * self.space.size 57 | directions = self.rng.uniform(-1, 1, size=(population_size, 2)) 58 | Boid.create_agents( 59 | self, 60 | population_size, 61 | self.space, 62 | position=positions, 63 | direction=directions, 64 | cohere=cohere, 65 | separate=separate, 66 | match=match, 67 | speed=speed, 68 | vision=vision, 69 | separation=separation, 70 | ) 71 | 72 | # For tracking statistics 73 | self.average_heading = None 74 | self.update_average_heading() 75 | 76 | def update_average_heading(self): 77 | """Calculate the average heading (direction) of all Boids.""" 78 | if not self.agents: 79 | self.average_heading = 0 80 | return 81 | 82 | headings = np.array([agent.direction for agent in self.agents]) 83 | mean_heading = np.mean(headings, axis=0) 84 | self.average_heading = np.arctan2(mean_heading[1], mean_heading[0]) 85 | 86 | def step(self): 87 | """Run one step of the model. 88 | 89 | All agents are activated in random order using the AgentSet shuffle_do method. 90 | """ 91 | self.agents.shuffle_do("step") 92 | self.update_average_heading() 93 | -------------------------------------------------------------------------------- /Flocking/Mesa/boid.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from mesa.experimental.continuous_space import ContinuousSpaceAgent 4 | 5 | class Boid(ContinuousSpaceAgent): 6 | """ 7 | A Boid-style flocker agent. 8 | 9 | The agent follows three behaviors to flock: 10 | - Cohesion: steering towards neighboring agents. 11 | - Separation: avoiding getting too close to any other agent. 12 | - Alignment: try to fly in the same direction as the neighbors. 13 | 14 | Boids have a vision that defines the radius in which they look for their 15 | neighbors to flock with. Their speed (a scalar) and velocity (a vector) 16 | define their movement. Separation is their desired minimum distance from 17 | any other Boid. 18 | """ 19 | 20 | def __init__( 21 | self, 22 | model, 23 | space, 24 | position, 25 | speed, 26 | direction, 27 | vision, 28 | separation, 29 | cohere=0.03, 30 | separate=0.015, 31 | match=0.05, 32 | ): 33 | """ 34 | Create a new Boid flocker agent. 35 | 36 | Args: 37 | unique_id: Unique agent identifyer. 38 | pos: Starting position 39 | speed: Distance to move per step. 40 | heading: numpy vector for the Boid's direction of movement. 41 | vision: Radius to look around for nearby Boids. 42 | separation: Minimum distance to maintain from other Boids. 43 | cohere: the relative importance of matching neighbors' positions 44 | separate: the relative importance of avoiding close neighbors 45 | match: the relative importance of matching neighbors' headings 46 | 47 | """ 48 | super().__init__(space, model) 49 | self.position = position 50 | self.speed = speed 51 | self.direction = direction 52 | self.vision = vision 53 | self.separation = separation 54 | self.cohere_factor = cohere 55 | self.separate_factor = separate 56 | self.match_factor = match 57 | self.neighbors = [] 58 | 59 | def step(self): 60 | """ 61 | Get the Boid's neighbors, compute the new vector, and move accordingly. 62 | """ 63 | 64 | neighbors, distances = self.get_neighbors_in_radius(radius=self.vision) 65 | self.neighbors = [n for n in neighbors if n is not self] 66 | 67 | # If no neighbors, maintain current direction 68 | if not neighbors: 69 | self.position += self.direction * self.speed 70 | return 71 | 72 | delta = self.space.calculate_difference_vector(self.position, agents=neighbors) 73 | 74 | cohere_vector = delta.sum(axis=0) * self.cohere_factor 75 | separation_vector = ( 76 | -1 * delta[distances < self.separation].sum(axis=0) * self.separate_factor 77 | ) 78 | match_vector = ( 79 | np.asarray([n.direction for n in neighbors]).sum(axis=0) * self.match_factor 80 | ) 81 | 82 | # Update direction based on the three behaviors 83 | self.direction += (cohere_vector + separation_vector + match_vector) / len( 84 | neighbors 85 | ) 86 | 87 | # Normalize direction vector 88 | self.direction /= np.linalg.norm(self.direction) 89 | 90 | # Move boid 91 | self.position += self.direction * self.speed 92 | 93 | -------------------------------------------------------------------------------- /WolfSheep/Mason/Wsg_small.java: -------------------------------------------------------------------------------- 1 | import sim.engine.*; 2 | import sim.util.*; 3 | import sim.field.grid.IntGrid2D; 4 | import sim.field.grid.SparseGrid2D; 5 | 6 | public class Wsg_small extends SimState 7 | { 8 | public static final int GRID_HEIGHT = 25; 9 | public static final int GRID_WIDTH = 25; 10 | public SparseGrid2D fieldSheeps = new SparseGrid2D(GRID_WIDTH, GRID_HEIGHT); 11 | public SparseGrid2D fieldWolves = new SparseGrid2D(GRID_WIDTH, GRID_HEIGHT); 12 | public IntGrid2D fieldGrass = new IntGrid2D(GRID_WIDTH, GRID_HEIGHT); 13 | public IntGrid2D fieldFullyGrown = new IntGrid2D(GRID_WIDTH, GRID_HEIGHT); 14 | 15 | public int numWS = 100; 16 | public int numSheep = 60; 17 | public int numWolves = 40; 18 | 19 | public Wsg_small(long seed) 20 | { 21 | super(seed); 22 | } 23 | 24 | public void start() 25 | { 26 | super.start(); 27 | 28 | for (int i = 0; i < GRID_WIDTH; i++) { 29 | for (int j = 0; j < GRID_HEIGHT; j++) { 30 | if (random.nextDouble() < 0.5) { 31 | fieldGrass.set(i, j, random.nextInt(20)); 32 | fieldFullyGrown.set(i, j, 1); 33 | } else { 34 | fieldGrass.set(i, j, 0); 35 | fieldFullyGrown.set(i, j, 0); 36 | } 37 | } 38 | } 39 | 40 | for(int x=0;x None: 85 | """Set grass growth state and schedule regrowth if eaten.""" 86 | self._fully_grown = value 87 | 88 | if not value: # If grass was just eaten 89 | self.model.simulator.schedule_event_relative( 90 | setattr, 91 | self.grass_regrowth_time, 92 | function_args=[self, "fully_grown", True], 93 | ) 94 | 95 | def __init__(self, model, countdown, grass_regrowth_time, cell): 96 | """Create a new patch of grass. 97 | 98 | Args: 99 | model: Model instance 100 | countdown: Time until grass is fully grown again 101 | grass_regrowth_time: Time needed to regrow after being eaten 102 | cell: Cell to which this grass patch belongs 103 | """ 104 | super().__init__(model) 105 | self._fully_grown = countdown == 0 106 | self.grass_regrowth_time = grass_regrowth_time 107 | self.cell = cell 108 | 109 | # Schedule initial growth if not fully grown 110 | if not self.fully_grown: 111 | self.model.simulator.schedule_event_relative( 112 | setattr, countdown, function_args=[self, "fully_grown", True] 113 | ) 114 | -------------------------------------------------------------------------------- /Flocking/Mason/Flocker_large.java: -------------------------------------------------------------------------------- 1 | import sim.engine.SimState; 2 | import sim.util.Bag; 3 | import sim.field.continuous.Continuous2D; 4 | import sim.util.Double2D; 5 | import sim.engine.Steppable; 6 | 7 | public class Flocker_large implements Steppable 8 | { 9 | public Double2D loc; 10 | public Double2D lastd; 11 | public Continuous2D flockers; 12 | public Flocking_large theFlock; 13 | 14 | public Flocker_large(final Double2D location, final Double2D lastd) { 15 | this.lastd = lastd; 16 | this.loc = location; 17 | } 18 | 19 | public Bag getNeighbors() { 20 | return this.flockers.getNeighborsWithinDistance(this.loc, this.theFlock.neighborhood, true); 21 | } 22 | 23 | public Double2D momentum() { 24 | return this.lastd; 25 | } 26 | 27 | public Double2D consistency(final Bag b, final Continuous2D flockers) { 28 | double x = 0.0; 29 | double y = 0.0; 30 | int i = 0; 31 | int count = 0; 32 | for (i = 0; i < b.numObjs; ++i) { 33 | final Flocker_large other = (Flocker_large)b.objs[i]; 34 | if (other != this) { 35 | final Double2D m = ((Flocker_large)b.objs[i]).momentum(); 36 | x += m.x; 37 | y += m.y; 38 | ++count; 39 | } 40 | } 41 | if (count > 0) { 42 | x /= count; 43 | y /= count; 44 | } 45 | return new Double2D(x, y); 46 | } 47 | 48 | public Double2D cohesion(final Bag b, final Continuous2D flockers) { 49 | double x = 0.0; 50 | double y = 0.0; 51 | int count = 0; 52 | int i; 53 | Flocker_large other; 54 | double dx; 55 | double dy; 56 | for (i = 0; i < b.numObjs; ++i) { 57 | other = (Flocker_large)b.objs[i]; 58 | if (other != this) { 59 | dx = other.loc.x - this.loc.x; 60 | dy = other.loc.y - this.loc.y; 61 | x += dx; 62 | y += dy; 63 | ++count; 64 | } 65 | } 66 | if (count > 0) { 67 | x /= count; 68 | y /= count; 69 | } 70 | return new Double2D(x, y); 71 | } 72 | 73 | public Double2D avoidance(final Bag b, final Continuous2D flockers) { 74 | double x = 0.0; 75 | double y = 0.0; 76 | int i = 0; 77 | int count = 0; 78 | for (i = 0; i < b.numObjs; ++i) { 79 | final Flocker_large other = (Flocker_large)b.objs[i]; 80 | if (other != this) { 81 | final double dx = other.loc.x - this.loc.x; 82 | final double dy = other.loc.y - this.loc.y; 83 | final double lensquared = dx * dx + dy * dy; 84 | ++count; 85 | if (lensquared < 1.0) { 86 | x -= dx; 87 | y -= dy; 88 | } 89 | } 90 | } 91 | if (count > 0) { 92 | x /= count; 93 | y /= count; 94 | } 95 | return new Double2D(x, y); 96 | } 97 | 98 | public void step(final SimState state) { 99 | final Flocking_large flock = (Flocking_large)state; 100 | this.loc = flock.flockers.getObjectLocation((Object)this); 101 | final Bag b = this.getNeighbors(); 102 | final Double2D avoid = this.avoidance(b, flock.flockers); 103 | final Double2D cohe = this.cohesion(b, flock.flockers); 104 | final Double2D cons = this.consistency(b, flock.flockers); 105 | final Double2D mome = this.momentum(); 106 | double dx = flock.cohesion * cohe.x + flock.avoidance * avoid.x + flock.consistency * cons.x + mome.x; 107 | double dy = flock.cohesion * cohe.y + flock.avoidance * avoid.y + flock.consistency * cons.y + mome.y; 108 | final double dis = Math.sqrt(dx * dx + dy * dy); 109 | if (dis > 0.0) { 110 | dx = dx / dis; 111 | dy = dy / dis; 112 | } 113 | this.lastd = new Double2D(dx, dy); 114 | this.loc = new Double2D(flock.flockers.stx(this.loc.x + dx), flock.flockers.sty(this.loc.y + dy)); 115 | flock.flockers.setObjectLocation((Object)this, this.loc); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Flocking/Mason/Flocker_small.java: -------------------------------------------------------------------------------- 1 | import sim.engine.SimState; 2 | import sim.util.Bag; 3 | import sim.field.continuous.Continuous2D; 4 | import sim.util.Double2D; 5 | import sim.engine.Steppable; 6 | 7 | public class Flocker_small implements Steppable 8 | { 9 | public Double2D loc; 10 | public Double2D lastd; 11 | public Continuous2D flockers; 12 | public Flocking_small theFlock; 13 | 14 | public Flocker_small(final Double2D location, final Double2D lastd) { 15 | this.lastd = lastd; 16 | this.loc = location; 17 | } 18 | 19 | public Bag getNeighbors() { 20 | return this.flockers.getNeighborsWithinDistance(this.loc, this.theFlock.neighborhood, true); 21 | } 22 | 23 | public Double2D momentum() { 24 | return this.lastd; 25 | } 26 | 27 | public Double2D consistency(final Bag b, final Continuous2D flockers) { 28 | double x = 0.0; 29 | double y = 0.0; 30 | int i = 0; 31 | int count = 0; 32 | for (i = 0; i < b.numObjs; ++i) { 33 | final Flocker_small other = (Flocker_small)b.objs[i]; 34 | if (other != this) { 35 | final Double2D m = ((Flocker_small)b.objs[i]).momentum(); 36 | x += m.x; 37 | y += m.y; 38 | ++count; 39 | } 40 | } 41 | if (count > 0) { 42 | x /= count; 43 | y /= count; 44 | } 45 | return new Double2D(x, y); 46 | } 47 | 48 | public Double2D cohesion(final Bag b, final Continuous2D flockers) { 49 | double x = 0.0; 50 | double y = 0.0; 51 | int count = 0; 52 | int i; 53 | Flocker_small other; 54 | double dx; 55 | double dy; 56 | for (i = 0; i < b.numObjs; ++i) { 57 | other = (Flocker_small)b.objs[i]; 58 | if (other != this) { 59 | dx = other.loc.x - this.loc.x; 60 | dy = other.loc.y - this.loc.y; 61 | x += dx; 62 | y += dy; 63 | ++count; 64 | } 65 | } 66 | if (count > 0) { 67 | x /= count; 68 | y /= count; 69 | } 70 | return new Double2D(x, y); 71 | } 72 | 73 | public Double2D avoidance(final Bag b, final Continuous2D flockers) { 74 | double x = 0.0; 75 | double y = 0.0; 76 | int i = 0; 77 | int count = 0; 78 | for (i = 0; i < b.numObjs; ++i) { 79 | final Flocker_small other = (Flocker_small)b.objs[i]; 80 | if (other != this) { 81 | final double dx = other.loc.x - this.loc.x; 82 | final double dy = other.loc.y - this.loc.y; 83 | final double lensquared = dx * dx + dy * dy; 84 | ++count; 85 | if (lensquared < 1.0) { 86 | x -= dx; 87 | y -= dy; 88 | } 89 | } 90 | } 91 | if (count > 0) { 92 | x /= count; 93 | y /= count; 94 | } 95 | return new Double2D(x, y); 96 | } 97 | 98 | public void step(final SimState state) { 99 | final Flocking_small flock = (Flocking_small)state; 100 | this.loc = flock.flockers.getObjectLocation((Object)this); 101 | final Bag b = this.getNeighbors(); 102 | final Double2D avoid = this.avoidance(b, flock.flockers); 103 | final Double2D cohe = this.cohesion(b, flock.flockers); 104 | final Double2D cons = this.consistency(b, flock.flockers); 105 | final Double2D mome = this.momentum(); 106 | double dx = flock.cohesion * cohe.x + flock.avoidance * avoid.x + flock.consistency * cons.x + mome.x; 107 | double dy = flock.cohesion * cohe.y + flock.avoidance * avoid.y + flock.consistency * cons.y + mome.y; 108 | final double dis = Math.sqrt(dx * dx + dy * dy); 109 | if (dis > 0.0) { 110 | dx = dx / dis; 111 | dy = dy / dis; 112 | } 113 | this.lastd = new Double2D(dx, dy); 114 | this.loc = new Double2D(flock.flockers.stx(this.loc.x + dx), flock.flockers.sty(this.loc.y + dy)); 115 | flock.flockers.setObjectLocation((Object)this, this.loc); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Flocking/DECLARATION.md: -------------------------------------------------------------------------------- 1 | # Flocking model 2 | 3 | An archetypical model of flocking behaviour of animals in continuous space. It is meant to test the performance of the software in continuous space: handling 64-bit floats, moving agents, searching for nearby agents. 4 | 5 | Note that there are two versions of this model, a small one and a large one. The difference in the large is that the space extent, visual distance, and number of agents, are larger. 6 | 7 | The flock model illustrates how flocking behaviour can emerge when each bird follows three simple rules: 8 | 9 | * maintain a minimum distance from other birds to avoid collision. 10 | * fly towards the average position of neighbors. 11 | * fly in the average direction of neighbors. 12 | 13 | ## Rules of the ABM 14 | 15 | - Agents live in a 2 dimensional continuous space. This means that the agent position is a 64 bit float and that the extent of the space the agents live in is also a 64 bit float. 16 | - The space is periodic in both directions: if an agent moves outside the space extent, the agent wraps back to the other side. Additionally, when searching for nearby agents, the periodicity of the space must be respected. 17 | - Agents represent birds trying to flock. They have many properties that are listed below, including the type each property should have. See the technical implementation below for default values (the properties may be named arbitrarily by each implementation): 18 | - `ID`, integer, a unique identifier for each agent. 19 | - `pos`, a tuple/vector of 2 64-bit floats, the position of the agent. 20 | - `vel`, a tuple/vector of 2 64-bit floats, the velocity of the agent. The rules of the ABM enforce `vel` to always be a unit vector. 21 | - `speed`, 64-bit number, the speed of the agent (magnitude of velocity). Defines how much an agent will move in one simulation step. 22 | - `separation`, 64-bit real, defines the minimum distance a bird must maintain from its neighbors. 23 | - `visual_distance` refers to the distance a bird can see and defines a radius of neighboring birds. 24 | - `cohere_factor`, 64-bit number, is the importance of maintaining the average position of neighbors. 25 | - `match_factor`, 64bit real, is the importance of matching the average trajectory of neighboring birds. 26 | - `separate_factor`, 64bit float, is the importance of maintaining the minimum distance from neighboring birds. 27 | - Agents are activated in sequence one by one in a random order (a new random order must be generated at each step of the simulation). Once activated, each agent does the following: 28 | - Finds all neighbors within its visual distance (i.e. finds all neighbors with euclidean distance less or equal to the value of `visual_distance`). 29 | - Three orientation vectors are generated from the neighbors: a coherence, a matching, and a separation vector. How these vectors are generated is actually simpler to show in code, see technical implementation below. 30 | - The agent updates its velocity based on the three orientation vectors and its own velocity as the average of the four vectors. The velocity vector is then normalized to unit length. 31 | - The agent moves according to the direction of its velocity vector for a length given by the `speed` agent property. 32 | - The simulation is performed for a fixed amount of steps. 33 | 34 | ## Technical implementation 35 | 36 | The simulation is performed for exactly 100 steps. 37 | 38 | Defaults that are the same for the small and large version of simulation: 39 | 40 | - speed of birds: 1.0 41 | - cohere factor of birds: 0.03 42 | - separation of birds: 1.0 43 | - separate factor of birds: 0.015 44 | - match factor of birds: 0.05 45 | 46 | Defaults for the small version: 47 | 48 | - dimensions of the space: (100, 100) 49 | - number of birds: 200 50 | - visual distance of birds: 5.0 51 | 52 | Defaults for the large version: 53 | 54 | - dimensions of the space: (150, 150) 55 | - number of birds: 400 56 | - visual distance of birds: 15.0 57 | 58 | Here is the code for the update rule of the flocking behaviour in Agents.jl: 59 | ```julia 60 | function flocking_agent_step!(bird, model) 61 | neighbor_ids = nearby_ids(bird, model, bird.visual_distance) 62 | N = 0 63 | match = separate = cohere = (0.0, 0.0) 64 | for id in neighbor_ids 65 | N += 1 66 | neighbor = model[id].pos 67 | heading = neighbor .- bird.pos 68 | cohere = cohere .+ heading 69 | if euclidean_distance(bird.pos, neighbor, model) < bird.separation 70 | separate = separate .- heading 71 | end 72 | match = match .+ model[id].vel 73 | end 74 | N = max(N, 1) 75 | cohere = cohere ./ N .* bird.cohere_factor 76 | separate = separate ./ N .* bird.separate_factor 77 | match = match ./ N .* bird.match_factor 78 | bird.vel = (bird.vel .+ cohere .+ separate .+ match) ./ 2 79 | bird.vel = bird.vel ./ norm(bird.vel) 80 | move_agent!(bird, model, bird.speed) 81 | end 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agent based modelling frameworks comparison 2 | 3 | 4 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8016506.svg)](https://doi.org/10.5281/zenodo.8016506) 5 | 6 | 7 | 8 | This repository contains code used to compare performance and features between various agent based modelling **(ABM)** frameworks. Currently, frameworks compared are [Agents.jl](https://github.com/JuliaDynamics/Agents.jl), [NetLogo](https://github.com/NetLogo/NetLogo), [MASON](https://github.com/eclab/mason) and [Mesa](https://github.com/projectmesa/mesa). We happily welcome more frameworks to join the comparison. 9 | 10 | **The performance benchmark comparison is run automatically during continuous integration, and hence the comparison is updated after every pull request to this repo.** 11 | 12 | This repository has been initiated and maintained by the developers of Agents.jl. However, it strongly welcomes contributions (in the form of Pull Requests) from developers or users of other modelling frameworks. Contributions may improve performance of a model implementation, or simplify the code of the implementation, provided that they still abide the model declaration, see below for more information. Furthermore, we also welcome contributions that may implement a comparison across a new agent based model not yet considered in this comparison. 13 | 14 | ## Latest results 15 | 16 | These are the results of the latest comparison: 17 | 18 | | Model/Framework | Agents.jl 6.2.10 | MASON 22.0 | Netlogo 6.4.0 | Mesa 3.2.0 | 19 | |:------------------|:---------------:|:------------:|:------------:|:---------------:| 20 | | WolfSheep (Time-Ratio, Small-Version) | 1 | 73.3x | 12.8x | 12.4x | 21 | | WolfSheep (Time-Ratio, Large-Version) | 1 | 17.1x | 6.1x | 4.9x | 22 | | WolfSheep (Lines of Code) | 73 | 202 | 137 (871) | 118 | 23 | | Flocking (Time-Ratio, Small-Version) | 1 | 17.7x | 17.1x | 184.1x | 24 | | Flocking (Time-Ratio, Large-Version) | 1 | 2.8x | 19.0x | 62.7x | 25 | | Flocking (Lines of Code) | 42 | 159 | 82 (689) | 94 | 26 | | Schelling (Time-Ratio, Small-Version) | 1 | 43.3x | 12.5x | 32.3x | 27 | | Schelling (Time-Ratio, Large-Version) | 1 | 3.7x | 13.5x | 23.4x | 28 | | Schelling (Lines of Code) | 26 | 129 | 54 (739) | 33 | 29 | 30 | ## How it works 31 | 32 | Various agent based models have been selected to compare performance, such as the Schelling model for example. This repository is structured as follows 33 | 34 | 1. Each selected ABM is contained in a dedicated folder of this repo. 35 | 1. Inside the ABM folder there is a DECLARATION.md markdown file. In declares both the scientific as well as technical implementation of the ABM. We tried our best to make the declaration as clear and as specific as possible, but we welcome Pull Requests that may clarify the declaration even more. 36 | 1. In the same folder there are subfolders named after the frameworks. Each contains the files that implement and benchmark the ABM implementation. 37 | 1. The implementation must be written in the same way a typical user will use the respective software. The implementations must only use the documented API of the respective software. 38 | 1. The benchmark step operates as follows: all models must be seeded with a given random number generator seed. At the start of the process, _`S` random seeds are generated in a reproducible way (or, alternatively, a random number generator that generates seeds is initialized with a specified seed)_. `S` is the amount of random seeds and hence also the amount of simulations performed for a given model. Unless stated otherwise in the declaration file of an ABM, `S` has been arbitrarily decided to be `100`. 39 | 1. From these `100` random (but reproducibly random) model runs, the median is used as the performance of each software. 40 | 1. The benchmarks are run during continuous integration. The benchmark timings are collected among ABMs and among the different ABM software during continuous integration. The timings are printed in the CI log, and also stored in a csv file (not yet, TODO) to be accessed later. 41 | 42 | ## How to run the benchmarks locally 43 | 44 | To reproduce the results you can run the `runall.sh` file with `bash runall.sh`. It is easier to run the file with a Linux OS, but you can emulate the same behaviour on Windows using WSL. 45 | 46 | The requirements to run the benchmark file are: 47 | 48 | 1. To run the file on a bash shell; 49 | 1. To install the tested frameworks (except for Mason which is already provided); 50 | 1. To make the commands `julia`, `python`, `java` and `javac` available from the shell and to have the bc tool available; 51 | 1. To move the folder where NetLogo is installed, rename it as `netlogo` and put it inside the main folder. 52 | 53 | This snippet was tested on an Ubuntu 22.04 LTS x86_64, but it should work also on other similar environments, copy-paste it on a bash shell to set up everything automatically for the benchmark: 54 | 55 | ```bash 56 | # fetch update software list 57 | sudo apt-get update 58 | 59 | # clone the repository and give permissions 60 | sudo git clone https://github.com/JuliaDynamics/ABM_Framework_Comparisons.git 61 | sudo chmod a+rwx ABM_Framework_Comparisons 62 | sudo chmod -R 777 ABM_Framework_Comparisons 63 | 64 | # install julia 65 | sudo wget https://julialang-s3.julialang.org/bin/linux/x64/1.9/julia-1.11.5-linux-x86_64.tar.gz 66 | sudo tar zxvf julia-1.9.4-linux-x86_64.tar.gz 67 | export PATH=$PATH:$(pwd)"/julia-1.11.5/bin" 68 | printf "\nexport PATH=\"\$PATH:"$(pwd)"/julia-1.11.5/bin\"" >> ~/.bashrc 69 | 70 | # install agents 71 | julia --project=ABM_Framework_Comparisons -e 'using Pkg; Pkg.instantiate()' 72 | 73 | # install java 74 | sudo apt install default-jre-headless 75 | sudo apt install default-jdk-headless 76 | 77 | # install mesa 78 | sudo apt install python3-pip 79 | pip install mesa==3.2.0 80 | 81 | # install netlogo 82 | sudo wget http://ccl.northwestern.edu/netlogo/6.4.0/NetLogo-6.4.0-64.tgz 83 | sudo tar -xzf NetLogo-6.4.0-64.tgz 84 | 85 | # move netlogo inside repository 86 | sudo mv "NetLogo-6.4.0-64" netlogo 87 | sudo mv netlogo ABM_Framework_Comparisons 88 | 89 | # install bc tools 90 | sudo apt install bc 91 | 92 | # move to repo folder 93 | cd ABM_Framework_Comparisons 94 | ``` 95 | 96 | If you are using WSL make sure that you move to a folder inside the subsystem before running these commands. 97 | 98 | -------------------------------------------------------------------------------- /Flocking/NetLogo/Flocking.nlogo: -------------------------------------------------------------------------------- 1 | extensions [ profiler ] 2 | 3 | globals [ 4 | time-tot 5 | ] 6 | 7 | turtles-own [ 8 | flockmates ;; agentset of nearby turtles 9 | nearest-neighbor ;; closest one of our flockmates 10 | ] 11 | 12 | to benchmark 13 | profiler:reset ;; clear the data 14 | profiler:start ;; start profiling 15 | setup 16 | repeat n_ticks [go] ;; run for n_ticks steps 17 | profiler:stop ;; stop profiling 18 | set time-tot profiler:inclusive-time "go" + profiler:inclusive-time "setup" 19 | file-open "times.txt" 20 | file-print time-tot 21 | file-close 22 | end 23 | 24 | to setup 25 | clear-all 26 | random-seed seed 27 | create-turtles population 28 | [ set color yellow - 2 + random 7 ;; random shades look nice 29 | set size 2 ;; easier to see 30 | setxy random-xcor random-ycor 31 | set flockmates no-turtles ] 32 | set time-tot 0 33 | reset-ticks 34 | end 35 | 36 | to go 37 | ask turtles [ flock ] 38 | ;; the following line is used to make the turtles 39 | ;; animate more smoothly. 40 | ;repeat 5 [ ask turtles [ fd 0.2 ] display ] 41 | ;; for greater efficiency, at the expense of smooth 42 | ;; animation, substitute the following line instead: 43 | ask turtles [ fd 1 ] 44 | tick 45 | end 46 | 47 | to flock ;; turtle procedure 48 | find-flockmates 49 | if any? flockmates 50 | [ find-nearest-neighbor 51 | ifelse distance nearest-neighbor < minimum-separation 52 | [ separate ] 53 | [ align 54 | cohere ] ] 55 | end 56 | 57 | to find-flockmates ;; turtle procedure 58 | set flockmates other turtles in-radius vision 59 | end 60 | 61 | to find-nearest-neighbor ;; turtle procedure 62 | set nearest-neighbor min-one-of flockmates [distance myself] 63 | end 64 | 65 | ;;; SEPARATE 66 | 67 | to separate ;; turtle procedure 68 | turn-away ([heading] of nearest-neighbor) max-separate-turn 69 | end 70 | 71 | ;;; ALIGN 72 | 73 | to align ;; turtle procedure 74 | turn-towards average-flockmate-heading max-align-turn 75 | end 76 | 77 | to-report average-flockmate-heading ;; turtle procedure 78 | ;; We can't just average the heading variables here. 79 | ;; For example, the average of 1 and 359 should be 0, 80 | ;; not 180. So we have to use trigonometry. 81 | let x-component sum [dx] of flockmates 82 | let y-component sum [dy] of flockmates 83 | ifelse x-component = 0 and y-component = 0 84 | [ report heading ] 85 | [ report atan x-component y-component ] 86 | end 87 | 88 | ;;; COHERE 89 | 90 | to cohere ;; turtle procedure 91 | turn-towards average-heading-towards-flockmates max-cohere-turn 92 | end 93 | 94 | to-report average-heading-towards-flockmates ;; turtle procedure 95 | ;; "towards myself" gives us the heading from the other turtle 96 | ;; to me, but we want the heading from me to the other turtle, 97 | ;; so we add 180 98 | let x-component mean [sin (towards myself + 180)] of flockmates 99 | let y-component mean [cos (towards myself + 180)] of flockmates 100 | ifelse x-component = 0 and y-component = 0 101 | [ report heading ] 102 | [ report atan x-component y-component ] 103 | end 104 | 105 | ;;; HELPER PROCEDURES 106 | 107 | to turn-towards [new-heading max-turn] ;; turtle procedure 108 | turn-at-most (subtract-headings new-heading heading) max-turn 109 | end 110 | 111 | to turn-away [new-heading max-turn] ;; turtle procedure 112 | turn-at-most (subtract-headings heading new-heading) max-turn 113 | end 114 | 115 | ;; turn right by "turn" degrees (or left if "turn" is negative), 116 | ;; but never turn more than "max-turn" degrees 117 | to turn-at-most [turn max-turn] ;; turtle procedure 118 | ifelse abs turn > max-turn 119 | [ ifelse turn > 0 120 | [ rt max-turn ] 121 | [ lt max-turn ] ] 122 | [ rt turn ] 123 | end 124 | 125 | 126 | ; Copyright 1998 Uri Wilensky. 127 | ; See Info tab for full copyright and license. 128 | @#$#@#$#@ 129 | GRAPHICS-WINDOW 130 | 250 131 | 10 132 | 1058 133 | 819 134 | -1 135 | -1 136 | 8.0 137 | 1 138 | 10 139 | 1 140 | 1 141 | 1 142 | 0 143 | 1 144 | 1 145 | 1 146 | 0 147 | 99 148 | 0 149 | 99 150 | 1 151 | 1 152 | 1 153 | ticks 154 | 30.0 155 | 156 | BUTTON 157 | 39 158 | 93 159 | 116 160 | 126 161 | NIL 162 | setup 163 | NIL 164 | 1 165 | T 166 | OBSERVER 167 | NIL 168 | NIL 169 | NIL 170 | NIL 171 | 1 172 | 173 | BUTTON 174 | 122 175 | 93 176 | 203 177 | 126 178 | NIL 179 | go 180 | T 181 | 1 182 | T 183 | OBSERVER 184 | NIL 185 | NIL 186 | NIL 187 | NIL 188 | 0 189 | 190 | SLIDER 191 | 9 192 | 51 193 | 232 194 | 84 195 | population 196 | population 197 | 1.0 198 | 1000.0 199 | 300.0 200 | 1.0 201 | 1 202 | NIL 203 | HORIZONTAL 204 | 205 | SLIDER 206 | 5 207 | 196 208 | 179 209 | 229 210 | seed 211 | seed 212 | 0.0 213 | 50000.0 214 | 1.0 215 | 1.0 216 | 1 217 | NIL 218 | HORIZONTAL 219 | 220 | SLIDER 221 | 5 222 | 196 223 | 179 224 | 229 225 | n_ticks 226 | n_ticks 227 | 0.0 228 | 50000.0 229 | 1.0 230 | 1.0 231 | 1 232 | NIL 233 | HORIZONTAL 234 | 235 | SLIDER 236 | 4 237 | 217 238 | 237 239 | 250 240 | max-align-turn 241 | max-align-turn 242 | 0.0 243 | 20.0 244 | 5.0 245 | 0.25 246 | 1 247 | degrees 248 | HORIZONTAL 249 | 250 | SLIDER 251 | 4 252 | 251 253 | 237 254 | 284 255 | max-cohere-turn 256 | max-cohere-turn 257 | 0.0 258 | 20.0 259 | 3.0 260 | 0.25 261 | 1 262 | degrees 263 | HORIZONTAL 264 | 265 | SLIDER 266 | 4 267 | 285 268 | 237 269 | 318 270 | max-separate-turn 271 | max-separate-turn 272 | 0.0 273 | 20.0 274 | 1.5 275 | 0.25 276 | 1 277 | degrees 278 | HORIZONTAL 279 | 280 | SLIDER 281 | 9 282 | 135 283 | 232 284 | 168 285 | vision 286 | vision 287 | 0.0 288 | 10.0 289 | 5.0 290 | 0.5 291 | 1 292 | patches 293 | HORIZONTAL 294 | 295 | SLIDER 296 | 9 297 | 169 298 | 232 299 | 202 300 | minimum-separation 301 | minimum-separation 302 | 0.0 303 | 5.0 304 | 1.0 305 | 0.25 306 | 1 307 | patches 308 | HORIZONTAL 309 | 310 | BUTTON 311 | 49 312 | 12 313 | 161 314 | 45 315 | benchmark 316 | benchmark 317 | NIL 318 | 1 319 | T 320 | OBSERVER 321 | NIL 322 | NIL 323 | NIL 324 | NIL 325 | 1 326 | 327 | @#$#@#$#@ 328 | ## WHAT IS IT? 329 | 330 | This model is an attempt to mimic the flocking of birds. (The resulting motion also resembles schools of fish.) The flocks that appear in this model are not created or led in any way by special leader birds. Rather, each bird is following exactly the same set of rules, from which flocks emerge. 331 | 332 | ## HOW IT WORKS 333 | 334 | The birds follow three rules: "alignment", "separation", and "cohesion". 335 | 336 | "Alignment" means that a bird tends to turn so that it is moving in the same direction that nearby birds are moving. 337 | 338 | "Separation" means that a bird will turn to avoid another bird which gets too close. 339 | 340 | "Cohesion" means that a bird will move towards other nearby birds (unless another bird is too close). 341 | 342 | When two birds are too close, the "separation" rule overrides the other two, which are deactivated until the minimum separation is achieved. 343 | 344 | The three rules affect only the bird's heading. Each bird always moves forward at the same constant speed. 345 | 346 | ## HOW TO USE IT 347 | 348 | First, determine the number of birds you want in the simulation and set the POPULATION slider to that value. Press SETUP to create the birds, and press GO to have them start flying around. 349 | 350 | The default settings for the sliders will produce reasonably good flocking behavior. However, you can play with them to get variations: 351 | 352 | Three TURN-ANGLE sliders control the maximum angle a bird can turn as a result of each rule. 353 | 354 | VISION is the distance that each bird can see 360 degrees around it. 355 | 356 | ## THINGS TO NOTICE 357 | 358 | Central to the model is the observation that flocks form without a leader. 359 | 360 | There are no random numbers used in this model, except to position the birds initially. The fluid, lifelike behavior of the birds is produced entirely by deterministic rules. 361 | 362 | Also, notice that each flock is dynamic. A flock, once together, is not guaranteed to keep all of its members. Why do you think this is? 363 | 364 | After running the model for a while, all of the birds have approximately the same heading. Why? 365 | 366 | Sometimes a bird breaks away from its flock. How does this happen? You may need to slow down the model or run it step by step in order to observe this phenomenon. 367 | 368 | ## THINGS TO TRY 369 | 370 | Play with the sliders to see if you can get tighter flocks, looser flocks, fewer flocks, more flocks, more or less splitting and joining of flocks, more or less rearranging of birds within flocks, etc. 371 | 372 | You can turn off a rule entirely by setting that rule's angle slider to zero. Is one rule by itself enough to produce at least some flocking? What about two rules? What's missing from the resulting behavior when you leave out each rule? 373 | 374 | Will running the model for a long time produce a static flock? Or will the birds never settle down to an unchanging formation? Remember, there are no random numbers used in this model. 375 | 376 | ## EXTENDING THE MODEL 377 | 378 | Currently the birds can "see" all around them. What happens if birds can only see in front of them? The `in-cone` primitive can be used for this. 379 | 380 | Is there some way to get V-shaped flocks, like migrating geese? 381 | 382 | What happens if you put walls around the edges of the world that the birds can't fly into? 383 | 384 | Can you get the birds to fly around obstacles in the middle of the world? 385 | 386 | What would happen if you gave the birds different velocities? For example, you could make birds that are not near other birds fly faster to catch up to the flock. Or, you could simulate the diminished air resistance that birds experience when flying together by making them fly faster when in a group. 387 | 388 | Are there other interesting ways you can make the birds different from each other? There could be random variation in the population, or you could have distinct "species" of bird. 389 | 390 | ## NETLOGO FEATURES 391 | 392 | Notice the need for the `subtract-headings` primitive and special procedure for averaging groups of headings. Just subtracting the numbers, or averaging the numbers, doesn't give you the results you'd expect, because of the discontinuity where headings wrap back to 0 once they reach 360. 393 | 394 | ## RELATED MODELS 395 | 396 | * Moths 397 | * Flocking Vee Formation 398 | * Flocking - Alternative Visualizations 399 | 400 | ## CREDITS AND REFERENCES 401 | 402 | This model is inspired by the Boids simulation invented by Craig Reynolds. The algorithm we use here is roughly similar to the original Boids algorithm, but it is not the same. The exact details of the algorithm tend not to matter very much -- as long as you have alignment, separation, and cohesion, you will usually get flocking behavior resembling that produced by Reynolds' original model. Information on Boids is available at http://www.red3d.com/cwr/boids/. 403 | 404 | ## HOW TO CITE 405 | 406 | If you mention this model or the NetLogo software in a publication, we ask that you include the citations below. 407 | 408 | For the model itself: 409 | 410 | * Wilensky, U. (1998). NetLogo Flocking model. http://ccl.northwestern.edu/netlogo/models/Flocking. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 411 | 412 | Please cite the NetLogo software as: 413 | 414 | * Wilensky, U. (1999). NetLogo. http://ccl.northwestern.edu/netlogo/. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 415 | 416 | ## COPYRIGHT AND LICENSE 417 | 418 | Copyright 1998 Uri Wilensky. 419 | 420 | ![CC BY-NC-SA 3.0](http://ccl.northwestern.edu/images/creativecommons/byncsa.png) 421 | 422 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 423 | 424 | Commercial licenses are also available. To inquire about commercial licenses, please contact Uri Wilensky at uri@northwestern.edu. 425 | 426 | This model was created as part of the project: CONNECTED MATHEMATICS: MAKING SENSE OF COMPLEX PHENOMENA THROUGH BUILDING OBJECT-BASED PARALLEL MODELS (OBPML). The project gratefully acknowledges the support of the National Science Foundation (Applications of Advanced Technologies Program) -- grant numbers RED #9552950 and REC #9632612. 427 | 428 | This model was converted to NetLogo as part of the projects: PARTICIPATORY SIMULATIONS: NETWORK-BASED DESIGN FOR SYSTEMS LEARNING IN CLASSROOMS and/or INTEGRATED SIMULATION AND MODELING ENVIRONMENT. The project gratefully acknowledges the support of the National Science Foundation (REPP & ROLE programs) -- grant numbers REC #9814682 and REC-0126227. Converted from StarLogoT to NetLogo, 2002. 429 | 430 | 431 | @#$#@#$#@ 432 | default 433 | true 434 | 0 435 | Polygon -7500403 true true 150 5 40 250 150 205 260 250 436 | 437 | airplane 438 | true 439 | 0 440 | Polygon -7500403 true true 150 0 135 15 120 60 120 105 15 165 15 195 120 180 135 240 105 270 120 285 150 270 180 285 210 270 165 240 180 180 285 195 285 165 180 105 180 60 165 15 441 | 442 | arrow 443 | true 444 | 0 445 | Polygon -7500403 true true 150 0 0 150 105 150 105 293 195 293 195 150 300 150 446 | 447 | box 448 | false 449 | 0 450 | Polygon -7500403 true true 150 285 285 225 285 75 150 135 451 | Polygon -7500403 true true 150 135 15 75 150 15 285 75 452 | Polygon -7500403 true true 15 75 15 225 150 285 150 135 453 | Line -16777216 false 150 285 150 135 454 | Line -16777216 false 150 135 15 75 455 | Line -16777216 false 150 135 285 75 456 | 457 | bug 458 | true 459 | 0 460 | Circle -7500403 true true 96 182 108 461 | Circle -7500403 true true 110 127 80 462 | Circle -7500403 true true 110 75 80 463 | Line -7500403 true 150 100 80 30 464 | Line -7500403 true 150 100 220 30 465 | 466 | butterfly 467 | true 468 | 0 469 | Polygon -7500403 true true 150 165 209 199 225 225 225 255 195 270 165 255 150 240 470 | Polygon -7500403 true true 150 165 89 198 75 225 75 255 105 270 135 255 150 240 471 | Polygon -7500403 true true 139 148 100 105 55 90 25 90 10 105 10 135 25 180 40 195 85 194 139 163 472 | Polygon -7500403 true true 162 150 200 105 245 90 275 90 290 105 290 135 275 180 260 195 215 195 162 165 473 | Polygon -16777216 true false 150 255 135 225 120 150 135 120 150 105 165 120 180 150 165 225 474 | Circle -16777216 true false 135 90 30 475 | Line -16777216 false 150 105 195 60 476 | Line -16777216 false 150 105 105 60 477 | 478 | car 479 | false 480 | 0 481 | Polygon -7500403 true true 300 180 279 164 261 144 240 135 226 132 213 106 203 84 185 63 159 50 135 50 75 60 0 150 0 165 0 225 300 225 300 180 482 | Circle -16777216 true false 180 180 90 483 | Circle -16777216 true false 30 180 90 484 | Polygon -16777216 true false 162 80 132 78 134 135 209 135 194 105 189 96 180 89 485 | Circle -7500403 true true 47 195 58 486 | Circle -7500403 true true 195 195 58 487 | 488 | circle 489 | false 490 | 0 491 | Circle -7500403 true true 0 0 300 492 | 493 | circle 2 494 | false 495 | 0 496 | Circle -7500403 true true 0 0 300 497 | Circle -16777216 true false 30 30 240 498 | 499 | cow 500 | false 501 | 0 502 | Polygon -7500403 true true 200 193 197 249 179 249 177 196 166 187 140 189 93 191 78 179 72 211 49 209 48 181 37 149 25 120 25 89 45 72 103 84 179 75 198 76 252 64 272 81 293 103 285 121 255 121 242 118 224 167 503 | Polygon -7500403 true true 73 210 86 251 62 249 48 208 504 | Polygon -7500403 true true 25 114 16 195 9 204 23 213 25 200 39 123 505 | 506 | cylinder 507 | false 508 | 0 509 | Circle -7500403 true true 0 0 300 510 | 511 | dot 512 | false 513 | 0 514 | Circle -7500403 true true 90 90 120 515 | 516 | face happy 517 | false 518 | 0 519 | Circle -7500403 true true 8 8 285 520 | Circle -16777216 true false 60 75 60 521 | Circle -16777216 true false 180 75 60 522 | Polygon -16777216 true false 150 255 90 239 62 213 47 191 67 179 90 203 109 218 150 225 192 218 210 203 227 181 251 194 236 217 212 240 523 | 524 | face neutral 525 | false 526 | 0 527 | Circle -7500403 true true 8 7 285 528 | Circle -16777216 true false 60 75 60 529 | Circle -16777216 true false 180 75 60 530 | Rectangle -16777216 true false 60 195 240 225 531 | 532 | face sad 533 | false 534 | 0 535 | Circle -7500403 true true 8 8 285 536 | Circle -16777216 true false 60 75 60 537 | Circle -16777216 true false 180 75 60 538 | Polygon -16777216 true false 150 168 90 184 62 210 47 232 67 244 90 220 109 205 150 198 192 205 210 220 227 242 251 229 236 206 212 183 539 | 540 | fish 541 | false 542 | 0 543 | Polygon -1 true false 44 131 21 87 15 86 0 120 15 150 0 180 13 214 20 212 45 166 544 | Polygon -1 true false 135 195 119 235 95 218 76 210 46 204 60 165 545 | Polygon -1 true false 75 45 83 77 71 103 86 114 166 78 135 60 546 | Polygon -7500403 true true 30 136 151 77 226 81 280 119 292 146 292 160 287 170 270 195 195 210 151 212 30 166 547 | Circle -16777216 true false 215 106 30 548 | 549 | flag 550 | false 551 | 0 552 | Rectangle -7500403 true true 60 15 75 300 553 | Polygon -7500403 true true 90 150 270 90 90 30 554 | Line -7500403 true 75 135 90 135 555 | Line -7500403 true 75 45 90 45 556 | 557 | flower 558 | false 559 | 0 560 | Polygon -10899396 true false 135 120 165 165 180 210 180 240 150 300 165 300 195 240 195 195 165 135 561 | Circle -7500403 true true 85 132 38 562 | Circle -7500403 true true 130 147 38 563 | Circle -7500403 true true 192 85 38 564 | Circle -7500403 true true 85 40 38 565 | Circle -7500403 true true 177 40 38 566 | Circle -7500403 true true 177 132 38 567 | Circle -7500403 true true 70 85 38 568 | Circle -7500403 true true 130 25 38 569 | Circle -7500403 true true 96 51 108 570 | Circle -16777216 true false 113 68 74 571 | Polygon -10899396 true false 189 233 219 188 249 173 279 188 234 218 572 | Polygon -10899396 true false 180 255 150 210 105 210 75 240 135 240 573 | 574 | house 575 | false 576 | 0 577 | Rectangle -7500403 true true 45 120 255 285 578 | Rectangle -16777216 true false 120 210 180 285 579 | Polygon -7500403 true true 15 120 150 15 285 120 580 | Line -16777216 false 30 120 270 120 581 | 582 | leaf 583 | false 584 | 0 585 | Polygon -7500403 true true 150 210 135 195 120 210 60 210 30 195 60 180 60 165 15 135 30 120 15 105 40 104 45 90 60 90 90 105 105 120 120 120 105 60 120 60 135 30 150 15 165 30 180 60 195 60 180 120 195 120 210 105 240 90 255 90 263 104 285 105 270 120 285 135 240 165 240 180 270 195 240 210 180 210 165 195 586 | Polygon -7500403 true true 135 195 135 240 120 255 105 255 105 285 135 285 165 240 165 195 587 | 588 | line 589 | true 590 | 0 591 | Line -7500403 true 150 0 150 300 592 | 593 | line half 594 | true 595 | 0 596 | Line -7500403 true 150 0 150 150 597 | 598 | pentagon 599 | false 600 | 0 601 | Polygon -7500403 true true 150 15 15 120 60 285 240 285 285 120 602 | 603 | person 604 | false 605 | 0 606 | Circle -7500403 true true 110 5 80 607 | Polygon -7500403 true true 105 90 120 195 90 285 105 300 135 300 150 225 165 300 195 300 210 285 180 195 195 90 608 | Rectangle -7500403 true true 127 79 172 94 609 | Polygon -7500403 true true 195 90 240 150 225 180 165 105 610 | Polygon -7500403 true true 105 90 60 150 75 180 135 105 611 | 612 | plant 613 | false 614 | 0 615 | Rectangle -7500403 true true 135 90 165 300 616 | Polygon -7500403 true true 135 255 90 210 45 195 75 255 135 285 617 | Polygon -7500403 true true 165 255 210 210 255 195 225 255 165 285 618 | Polygon -7500403 true true 135 180 90 135 45 120 75 180 135 210 619 | Polygon -7500403 true true 165 180 165 210 225 180 255 120 210 135 620 | Polygon -7500403 true true 135 105 90 60 45 45 75 105 135 135 621 | Polygon -7500403 true true 165 105 165 135 225 105 255 45 210 60 622 | Polygon -7500403 true true 135 90 120 45 150 15 180 45 165 90 623 | 624 | square 625 | false 626 | 0 627 | Rectangle -7500403 true true 30 30 270 270 628 | 629 | square 2 630 | false 631 | 0 632 | Rectangle -7500403 true true 30 30 270 270 633 | Rectangle -16777216 true false 60 60 240 240 634 | 635 | star 636 | false 637 | 0 638 | Polygon -7500403 true true 151 1 185 108 298 108 207 175 242 282 151 216 59 282 94 175 3 108 116 108 639 | 640 | target 641 | false 642 | 0 643 | Circle -7500403 true true 0 0 300 644 | Circle -16777216 true false 30 30 240 645 | Circle -7500403 true true 60 60 180 646 | Circle -16777216 true false 90 90 120 647 | Circle -7500403 true true 120 120 60 648 | 649 | tree 650 | false 651 | 0 652 | Circle -7500403 true true 118 3 94 653 | Rectangle -6459832 true false 120 195 180 300 654 | Circle -7500403 true true 65 21 108 655 | Circle -7500403 true true 116 41 127 656 | Circle -7500403 true true 45 90 120 657 | Circle -7500403 true true 104 74 152 658 | 659 | triangle 660 | false 661 | 0 662 | Polygon -7500403 true true 150 30 15 255 285 255 663 | 664 | triangle 2 665 | false 666 | 0 667 | Polygon -7500403 true true 150 30 15 255 285 255 668 | Polygon -16777216 true false 151 99 225 223 75 224 669 | 670 | truck 671 | false 672 | 0 673 | Rectangle -7500403 true true 4 45 195 187 674 | Polygon -7500403 true true 296 193 296 150 259 134 244 104 208 104 207 194 675 | Rectangle -1 true false 195 60 195 105 676 | Polygon -16777216 true false 238 112 252 141 219 141 218 112 677 | Circle -16777216 true false 234 174 42 678 | Rectangle -7500403 true true 181 185 214 194 679 | Circle -16777216 true false 144 174 42 680 | Circle -16777216 true false 24 174 42 681 | Circle -7500403 false true 24 174 42 682 | Circle -7500403 false true 144 174 42 683 | Circle -7500403 false true 234 174 42 684 | 685 | turtle 686 | true 687 | 0 688 | Polygon -10899396 true false 215 204 240 233 246 254 228 266 215 252 193 210 689 | Polygon -10899396 true false 195 90 225 75 245 75 260 89 269 108 261 124 240 105 225 105 210 105 690 | Polygon -10899396 true false 105 90 75 75 55 75 40 89 31 108 39 124 60 105 75 105 90 105 691 | Polygon -10899396 true false 132 85 134 64 107 51 108 17 150 2 192 18 192 52 169 65 172 87 692 | Polygon -10899396 true false 85 204 60 233 54 254 72 266 85 252 107 210 693 | Polygon -7500403 true true 119 75 179 75 209 101 224 135 220 225 175 261 128 261 81 224 74 135 88 99 694 | 695 | wheel 696 | false 697 | 0 698 | Circle -7500403 true true 3 3 294 699 | Circle -16777216 true false 30 30 240 700 | Line -7500403 true 150 285 150 15 701 | Line -7500403 true 15 150 285 150 702 | Circle -7500403 true true 120 120 60 703 | Line -7500403 true 216 40 79 269 704 | Line -7500403 true 40 84 269 221 705 | Line -7500403 true 40 216 269 79 706 | Line -7500403 true 84 40 221 269 707 | 708 | x 709 | false 710 | 0 711 | Polygon -7500403 true true 270 75 225 30 30 225 75 270 712 | Polygon -7500403 true true 30 75 75 30 270 225 225 270 713 | @#$#@#$#@ 714 | NetLogo 6.1.1 715 | @#$#@#$#@ 716 | set population 200 717 | setup 718 | repeat 200 [ go ] 719 | @#$#@#$#@ 720 | @#$#@#$#@ 721 | @#$#@#$#@ 722 | @#$#@#$#@ 723 | default 724 | 0.0 725 | -0.2 0 0.0 1.0 726 | 0.0 1 1.0 0.0 727 | 0.2 0 0.0 1.0 728 | link direction 729 | true 730 | 0 731 | Line -7500403 true 150 150 90 180 732 | Line -7500403 true 150 150 210 180 733 | @#$#@#$#@ 734 | 0 735 | @#$#@#$#@ 736 | -------------------------------------------------------------------------------- /Schelling/NetLogo/Schelling.nlogo: -------------------------------------------------------------------------------- 1 | extensions [profiler] 2 | 3 | globals [ 4 | time-tot 5 | ] 6 | 7 | turtles-own [ 8 | happy? ; for each turtle, indicates whether at least %-similar-wanted percent of that turtle's neighbors are the same color as the turtle 9 | similar-nearby ; how many neighboring patches have a turtle with my color? 10 | other-nearby ; how many have a turtle of another color? 11 | total-nearby ; sum of previous two variables 12 | ] 13 | 14 | to setup 15 | clear-all 16 | random-seed seed 17 | ; create turtles on random patches. 18 | ask patches [ set pcolor white ] 19 | create-turtles agents [ 20 | ; 105 is the color number for "blue" 21 | ; 27 is the color number for "orange" 22 | set color one-of [105 27] 23 | set size 1 24 | setxy random-pxcor random-pycor 25 | ] 26 | update-turtles 27 | set time-tot 0 28 | reset-ticks 29 | end 30 | 31 | to benchmark 32 | profiler:reset ;; clear the data 33 | profiler:start ;; start profiling 34 | setup 35 | repeat n_ticks [go] ;; run for n_ticks steps 36 | profiler:stop ;; stop profiling 37 | set time-tot profiler:inclusive-time "go" + profiler:inclusive-time "setup" 38 | file-open "times.txt" 39 | file-print time-tot 40 | file-close 41 | end 42 | 43 | ; run the model for one tick 44 | to go 45 | update-turtles 46 | tick 47 | end 48 | 49 | ; unhappy turtles try a new spot 50 | to move-unhappy-turtles 51 | ask turtles 52 | [ find-new-spot ] 53 | end 54 | 55 | to find-new-spot 56 | move-to one-of patches with [not any? turtles-here] 57 | end 58 | 59 | to update-turtles 60 | ask turtles [ 61 | ; in next two lines, we use "neighbors" to test the eight patches 62 | ; surrounding the current patch 63 | set similar-nearby count (other turtles-on patches in-radius r) with [ color = [ color ] of myself ] 64 | set other-nearby count (other turtles-on patches in-radius r) with [ color != [ color ] of myself ] 65 | set total-nearby similar-nearby + other-nearby 66 | set happy? similar-nearby >= (%-similar-wanted * total-nearby / 100) 67 | if not happy? [ find-new-spot ] 68 | ] 69 | end 70 | 71 | ; Copyright 1997 Uri Wilensky. 72 | ; See Info tab for full copyright and license. 73 | @#$#@#$#@ 74 | GRAPHICS-WINDOW 75 | 375 76 | 10 77 | 783 78 | 419 79 | -1 80 | -1 81 | 8.0 82 | 1 83 | 10 84 | 1 85 | 1 86 | 1 87 | 0 88 | 1 89 | 1 90 | 1 91 | 0 92 | 49 93 | 0 94 | 49 95 | 1 96 | 1 97 | 1 98 | ticks 99 | 30.0 100 | 101 | SLIDER 102 | 10 103 | 95 104 | 285 105 | 128 106 | %-similar-wanted 107 | %-similar-wanted 108 | 0 109 | 100 110 | 30.0 111 | 1 112 | 1 113 | % 114 | HORIZONTAL 115 | 116 | SLIDER 117 | 5 118 | 196 119 | 179 120 | 229 121 | r 122 | r 123 | 0.0 124 | 50000.0 125 | 1.0 126 | 1.0 127 | 1 128 | NIL 129 | HORIZONTAL 130 | 131 | BUTTON 132 | 10 133 | 55 134 | 90 135 | 88 136 | setup 137 | setup 138 | NIL 139 | 1 140 | T 141 | OBSERVER 142 | NIL 143 | NIL 144 | NIL 145 | NIL 146 | 1 147 | 148 | BUTTON 149 | 200 150 | 55 151 | 285 152 | 88 153 | go 154 | go 155 | T 156 | 1 157 | T 158 | OBSERVER 159 | NIL 160 | NIL 161 | NIL 162 | NIL 163 | 0 164 | 165 | BUTTON 166 | 100 167 | 55 168 | 190 169 | 88 170 | go once 171 | go 172 | NIL 173 | 1 174 | T 175 | OBSERVER 176 | NIL 177 | NIL 178 | NIL 179 | NIL 180 | 0 181 | 182 | CHOOSER 183 | 801 184 | 380 185 | 950 186 | 425 187 | visualization 188 | visualization 189 | "old" "square-x" 190 | 1 191 | 192 | SLIDER 193 | 5 194 | 196 195 | 179 196 | 229 197 | seed 198 | seed 199 | 0.0 200 | 50000.0 201 | 1.0 202 | 1.0 203 | 1 204 | NIL 205 | HORIZONTAL 206 | 207 | SLIDER 208 | 5 209 | 196 210 | 179 211 | 229 212 | n_ticks 213 | n_ticks 214 | 0.0 215 | 50000.0 216 | 1.0 217 | 1.0 218 | 1 219 | NIL 220 | HORIZONTAL 221 | 222 | SLIDER 223 | 10 224 | 10 225 | 285 226 | 43 227 | agents 228 | agents 229 | 50 230 | 5000 231 | 2000.0 232 | 1 233 | 1 234 | NIL 235 | HORIZONTAL 236 | 237 | PLOT 238 | 10 239 | 295 240 | 260 241 | 445 242 | Number-unhappy 243 | NIL 244 | NIL 245 | 0.0 246 | 10.0 247 | 0.0 248 | 100.0 249 | true 250 | false 251 | "" "" 252 | PENS 253 | "default" 1.0 0 -16777216 true "" "plot count turtles with [not happy?]" 254 | 255 | MONITOR 256 | 265 257 | 315 258 | 355 259 | 360 260 | num-unhappy 261 | count turtles with [not happy?] 262 | 1 263 | 1 264 | 11 265 | 266 | MONITOR 267 | 265 268 | 170 269 | 355 270 | 215 271 | # agents 272 | count turtles 273 | 1 274 | 1 275 | 11 276 | 277 | BUTTON 278 | 820 279 | 30 280 | 932 281 | 63 282 | benchmark 283 | benchmark 284 | NIL 285 | 1 286 | T 287 | OBSERVER 288 | NIL 289 | NIL 290 | NIL 291 | NIL 292 | 1 293 | 294 | @#$#@#$#@ 295 | ## WHAT IS IT? 296 | 297 | This project models the behavior of two types of agents in a neighborhood. The orange agents and blue agents get along with one another. But each agent wants to make sure that it lives near some of "its own." That is, each orange agent wants to live near at least some orange agents, and each blue agent wants to live near at least some blue agents. The simulation shows how these individual preferences ripple through the neighborhood, leading to large-scale patterns. 298 | 299 | This project was inspired by Thomas Schelling's writings about social systems (such as housing patterns in cities). 300 | 301 | ## HOW TO USE IT 302 | 303 | Click the SETUP button to set up the agents. There are approximately equal numbers of orange and blue agents. The agents are set up so no patch has more than one agent. Click GO to start the simulation. If agents don't have enough same-color neighbors, they move to a nearby patch. (The topology is wrapping, so that patches on the bottom edge are neighbors with patches on the top and similar for left and right). 304 | 305 | The DENSITY slider controls the occupancy density of the neighborhood (and thus the total number of agents). (It takes effect the next time you click SETUP.) The %-SIMILAR-WANTED slider controls the percentage of same-color agents that each agent wants among its neighbors. For example, if the slider is set at 30, each blue agent wants at least 30% of its neighbors to be blue agents. 306 | 307 | The % SIMILAR monitor shows the average percentage of same-color neighbors for each agent. It starts at about 50%, since each agent starts (on average) with an equal number of orange and blue agents as neighbors. The NUM-UNHAPPY monitor shows the number of unhappy agents, and the % UNHAPPY monitor shows the percent of agents that have fewer same-color neighbors than they want (and thus want to move). The % SIMILAR and the NUM-UNHAPPY monitors are also plotted. 308 | 309 | The VISUALIZATION chooser gives two options for visualizing the agents. The OLD option uses the visualization that was used by the segregation model in the past. The SQUARE-X option visualizes the agents as squares. Unhappy agents are visualized as Xs. 310 | 311 | ## THINGS TO NOTICE 312 | 313 | When you execute SETUP, the orange and blue agents are randomly distributed throughout the neighborhood. But many agents are "unhappy" since they don't have enough same-color neighbors. The unhappy agents move to new locations in the vicinity. But in the new locations, they might tip the balance of the local population, prompting other agents to leave. If a few agents move into an area, the local blue agents might leave. But when the blue agents move to a new area, they might prompt orange agents to leave that area. 314 | 315 | Over time, the number of unhappy agents decreases. But the neighborhood becomes more segregated, with clusters of orange agents and clusters of blue agents. 316 | 317 | In the case where each agent wants at least 30% same-color neighbors, the agents end up with (on average) 70% same-color neighbors. So relatively small individual preferences can lead to significant overall segregation. 318 | 319 | ## THINGS TO TRY 320 | 321 | Try different values for %-SIMILAR-WANTED. How does the overall degree of segregation change? 322 | 323 | If each agent wants at least 40% same-color neighbors, what percentage (on average) do they end up with? 324 | 325 | Try different values of DENSITY. How does the initial occupancy density affect the percentage of unhappy agents? How does it affect the time it takes for the model to finish? 326 | 327 | Can you set sliders so that the model never finishes running, and agents keep looking for new locations? 328 | 329 | ## EXTENDING THE MODEL 330 | 331 | The `find-new-spot` procedure has the agents move locally till they find a spot. Can you rewrite this procedure so the agents move directly to an appropriate new spot? 332 | 333 | Incorporate social networks into this model. For instance, have unhappy agents decide on a new location based on information about what a neighborhood is like from other agents in their network. 334 | 335 | Change the rules for agent happiness. One idea: suppose that the agents need some minimum threshold of "good neighbors" to be happy with their location. Suppose further that they don't always know if someone makes a good neighbor. When they do, they use that information. When they don't, they use color as a proxy -- i.e., they assume that agents of the same color make good neighbors. 336 | 337 | The two different visualizations emphasize different aspects of the model. The SQUARE-X visualization shows whether an agent is happy or not. Can you design a different visualization that emphasizes different aspects? 338 | 339 | ## NETLOGO FEATURES 340 | 341 | `sprout` is used to create agents while ensuring no patch has more than one agent on it. 342 | 343 | When an agent moves, `move-to` is used to move the agent to the center of the patch it eventually finds. 344 | 345 | Note two different methods that can be used for find-new-spot, one of them (the one we use) is recursive. 346 | 347 | ## CREDITS AND REFERENCES 348 | 349 | Schelling, T. (1978). Micromotives and Macrobehavior. New York: Norton. 350 | 351 | See also: Rauch, J. (2002). Seeing Around Corners; The Atlantic Monthly; April 2002;Volume 289, No. 4; 35-48. https://www.theatlantic.com/magazine/archive/2002/04/seeing-around-corners/302471/ 352 | 353 | ## HOW TO CITE 354 | 355 | If you mention this model or the NetLogo software in a publication, we ask that you include the citations below. 356 | 357 | For the model itself: 358 | 359 | * Wilensky, U. (1997). NetLogo Segregation model. http://ccl.northwestern.edu/netlogo/models/Segregation. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 360 | 361 | Please cite the NetLogo software as: 362 | 363 | * Wilensky, U. (1999). NetLogo. http://ccl.northwestern.edu/netlogo/. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 364 | 365 | ## COPYRIGHT AND LICENSE 366 | 367 | Copyright 1997 Uri Wilensky. 368 | 369 | ![CC BY-NC-SA 3.0](http://ccl.northwestern.edu/images/creativecommons/byncsa.png) 370 | 371 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 372 | 373 | Commercial licenses are also available. To inquire about commercial licenses, please contact Uri Wilensky at uri@northwestern.edu. 374 | 375 | This model was created as part of the project: CONNECTED MATHEMATICS: MAKING SENSE OF COMPLEX PHENOMENA THROUGH BUILDING OBJECT-BASED PARALLEL MODELS (OBPML). The project gratefully acknowledges the support of the National Science Foundation (Applications of Advanced Technologies Program) -- grant numbers RED #9552950 and REC #9632612. 376 | 377 | This model was converted to NetLogo as part of the projects: PARTICIPATORY SIMULATIONS: NETWORK-BASED DESIGN FOR SYSTEMS LEARNING IN CLASSROOMS and/or INTEGRATED SIMULATION AND MODELING ENVIRONMENT. The project gratefully acknowledges the support of the National Science Foundation (REPP & ROLE programs) -- grant numbers REC #9814682 and REC-0126227. Converted from StarLogoT to NetLogo, 2001. 378 | 379 | 380 | @#$#@#$#@ 381 | default 382 | true 383 | 0 384 | Polygon -7500403 true true 150 5 40 250 150 205 260 250 385 | 386 | airplane 387 | true 388 | 0 389 | Polygon -7500403 true true 150 0 135 15 120 60 120 105 15 165 15 195 120 180 135 240 105 270 120 285 150 270 180 285 210 270 165 240 180 180 285 195 285 165 180 105 180 60 165 15 390 | 391 | arrow 392 | true 393 | 0 394 | Polygon -7500403 true true 150 0 0 150 105 150 105 293 195 293 195 150 300 150 395 | 396 | box 397 | false 398 | 0 399 | Polygon -7500403 true true 150 285 285 225 285 75 150 135 400 | Polygon -7500403 true true 150 135 15 75 150 15 285 75 401 | Polygon -7500403 true true 15 75 15 225 150 285 150 135 402 | Line -16777216 false 150 285 150 135 403 | Line -16777216 false 150 135 15 75 404 | Line -16777216 false 150 135 285 75 405 | 406 | bug 407 | true 408 | 0 409 | Circle -7500403 true true 96 182 108 410 | Circle -7500403 true true 110 127 80 411 | Circle -7500403 true true 110 75 80 412 | Line -7500403 true 150 100 80 30 413 | Line -7500403 true 150 100 220 30 414 | 415 | butterfly 416 | true 417 | 0 418 | Polygon -7500403 true true 150 165 209 199 225 225 225 255 195 270 165 255 150 240 419 | Polygon -7500403 true true 150 165 89 198 75 225 75 255 105 270 135 255 150 240 420 | Polygon -7500403 true true 139 148 100 105 55 90 25 90 10 105 10 135 25 180 40 195 85 194 139 163 421 | Polygon -7500403 true true 162 150 200 105 245 90 275 90 290 105 290 135 275 180 260 195 215 195 162 165 422 | Polygon -16777216 true false 150 255 135 225 120 150 135 120 150 105 165 120 180 150 165 225 423 | Circle -16777216 true false 135 90 30 424 | Line -16777216 false 150 105 195 60 425 | Line -16777216 false 150 105 105 60 426 | 427 | car 428 | false 429 | 0 430 | Polygon -7500403 true true 300 180 279 164 261 144 240 135 226 132 213 106 203 84 185 63 159 50 135 50 75 60 0 150 0 165 0 225 300 225 300 180 431 | Circle -16777216 true false 180 180 90 432 | Circle -16777216 true false 30 180 90 433 | Polygon -16777216 true false 162 80 132 78 134 135 209 135 194 105 189 96 180 89 434 | Circle -7500403 true true 47 195 58 435 | Circle -7500403 true true 195 195 58 436 | 437 | circle 438 | false 439 | 0 440 | Circle -7500403 true true 0 0 300 441 | 442 | circle 2 443 | false 444 | 0 445 | Circle -7500403 true true 0 0 300 446 | Circle -16777216 true false 30 30 240 447 | 448 | cow 449 | false 450 | 0 451 | Polygon -7500403 true true 200 193 197 249 179 249 177 196 166 187 140 189 93 191 78 179 72 211 49 209 48 181 37 149 25 120 25 89 45 72 103 84 179 75 198 76 252 64 272 81 293 103 285 121 255 121 242 118 224 167 452 | Polygon -7500403 true true 73 210 86 251 62 249 48 208 453 | Polygon -7500403 true true 25 114 16 195 9 204 23 213 25 200 39 123 454 | 455 | cylinder 456 | false 457 | 0 458 | Circle -7500403 true true 0 0 300 459 | 460 | face happy 461 | false 462 | 0 463 | Circle -7500403 true true 8 8 285 464 | Circle -16777216 true false 60 75 60 465 | Circle -16777216 true false 180 75 60 466 | Polygon -16777216 true false 150 255 90 239 62 213 47 191 67 179 90 203 109 218 150 225 192 218 210 203 227 181 251 194 236 217 212 240 467 | 468 | face neutral 469 | false 470 | 0 471 | Circle -7500403 true true 8 7 285 472 | Circle -16777216 true false 60 75 60 473 | Circle -16777216 true false 180 75 60 474 | Rectangle -16777216 true false 60 195 240 225 475 | 476 | face sad 477 | false 478 | 0 479 | Circle -7500403 true true 8 8 285 480 | Circle -16777216 true false 60 75 60 481 | Circle -16777216 true false 180 75 60 482 | Polygon -16777216 true false 150 168 90 184 62 210 47 232 67 244 90 220 109 205 150 198 192 205 210 220 227 242 251 229 236 206 212 183 483 | 484 | face-happy 485 | false 486 | 0 487 | Circle -7500403 true true 8 8 285 488 | Circle -16777216 true false 60 75 60 489 | Circle -16777216 true false 180 75 60 490 | Polygon -16777216 true false 150 255 90 239 62 213 47 191 67 179 90 203 109 218 150 225 192 218 210 203 227 181 251 194 236 217 212 240 491 | 492 | fish 493 | false 494 | 0 495 | Polygon -1 true false 44 131 21 87 15 86 0 120 15 150 0 180 13 214 20 212 45 166 496 | Polygon -1 true false 135 195 119 235 95 218 76 210 46 204 60 165 497 | Polygon -1 true false 75 45 83 77 71 103 86 114 166 78 135 60 498 | Polygon -7500403 true true 30 136 151 77 226 81 280 119 292 146 292 160 287 170 270 195 195 210 151 212 30 166 499 | Circle -16777216 true false 215 106 30 500 | 501 | flag 502 | false 503 | 0 504 | Rectangle -7500403 true true 60 15 75 300 505 | Polygon -7500403 true true 90 150 270 90 90 30 506 | Line -7500403 true 75 135 90 135 507 | Line -7500403 true 75 45 90 45 508 | 509 | flower 510 | false 511 | 0 512 | Polygon -10899396 true false 135 120 165 165 180 210 180 240 150 300 165 300 195 240 195 195 165 135 513 | Circle -7500403 true true 85 132 38 514 | Circle -7500403 true true 130 147 38 515 | Circle -7500403 true true 192 85 38 516 | Circle -7500403 true true 85 40 38 517 | Circle -7500403 true true 177 40 38 518 | Circle -7500403 true true 177 132 38 519 | Circle -7500403 true true 70 85 38 520 | Circle -7500403 true true 130 25 38 521 | Circle -7500403 true true 96 51 108 522 | Circle -16777216 true false 113 68 74 523 | Polygon -10899396 true false 189 233 219 188 249 173 279 188 234 218 524 | Polygon -10899396 true false 180 255 150 210 105 210 75 240 135 240 525 | 526 | house 527 | false 528 | 0 529 | Rectangle -7500403 true true 45 120 255 285 530 | Rectangle -16777216 true false 120 210 180 285 531 | Polygon -7500403 true true 15 120 150 15 285 120 532 | Line -16777216 false 30 120 270 120 533 | 534 | leaf 535 | false 536 | 0 537 | Polygon -7500403 true true 150 210 135 195 120 210 60 210 30 195 60 180 60 165 15 135 30 120 15 105 40 104 45 90 60 90 90 105 105 120 120 120 105 60 120 60 135 30 150 15 165 30 180 60 195 60 180 120 195 120 210 105 240 90 255 90 263 104 285 105 270 120 285 135 240 165 240 180 270 195 240 210 180 210 165 195 538 | Polygon -7500403 true true 135 195 135 240 120 255 105 255 105 285 135 285 165 240 165 195 539 | 540 | line 541 | true 542 | 0 543 | Line -7500403 true 150 0 150 300 544 | 545 | line half 546 | true 547 | 0 548 | Line -7500403 true 150 0 150 150 549 | 550 | pentagon 551 | false 552 | 0 553 | Polygon -7500403 true true 150 15 15 120 60 285 240 285 285 120 554 | 555 | person 556 | false 557 | 0 558 | Circle -7500403 true true 110 5 80 559 | Polygon -7500403 true true 105 90 120 195 90 285 105 300 135 300 150 225 165 300 195 300 210 285 180 195 195 90 560 | Rectangle -7500403 true true 127 79 172 94 561 | Polygon -7500403 true true 195 90 240 150 225 180 165 105 562 | Polygon -7500403 true true 105 90 60 150 75 180 135 105 563 | 564 | person2 565 | false 566 | 0 567 | Circle -7500403 true true 105 0 90 568 | Polygon -7500403 true true 105 90 120 195 90 285 105 300 135 300 150 225 165 300 195 300 210 285 180 195 195 90 569 | Rectangle -7500403 true true 127 79 172 94 570 | Polygon -7500403 true true 195 90 285 180 255 210 165 105 571 | Polygon -7500403 true true 105 90 15 180 60 195 135 105 572 | 573 | plant 574 | false 575 | 0 576 | Rectangle -7500403 true true 135 90 165 300 577 | Polygon -7500403 true true 135 255 90 210 45 195 75 255 135 285 578 | Polygon -7500403 true true 165 255 210 210 255 195 225 255 165 285 579 | Polygon -7500403 true true 135 180 90 135 45 120 75 180 135 210 580 | Polygon -7500403 true true 165 180 165 210 225 180 255 120 210 135 581 | Polygon -7500403 true true 135 105 90 60 45 45 75 105 135 135 582 | Polygon -7500403 true true 165 105 165 135 225 105 255 45 210 60 583 | Polygon -7500403 true true 135 90 120 45 150 15 180 45 165 90 584 | 585 | square 586 | false 587 | 0 588 | Rectangle -7500403 true true 30 30 270 270 589 | 590 | square - happy 591 | false 592 | 0 593 | Rectangle -7500403 true true 30 30 270 270 594 | Polygon -16777216 false false 75 195 105 240 180 240 210 195 75 195 595 | 596 | square - unhappy 597 | false 598 | 0 599 | Rectangle -7500403 true true 30 30 270 270 600 | Polygon -16777216 false false 60 225 105 180 195 180 240 225 75 225 601 | 602 | square 2 603 | false 604 | 0 605 | Rectangle -7500403 true true 30 30 270 270 606 | Rectangle -16777216 true false 60 60 240 240 607 | 608 | square-small 609 | false 610 | 0 611 | Rectangle -7500403 true true 45 45 255 255 612 | 613 | square-x 614 | false 615 | 0 616 | Rectangle -7500403 true true 30 30 270 270 617 | Line -16777216 false 75 90 210 210 618 | Line -16777216 false 210 90 75 210 619 | 620 | star 621 | false 622 | 0 623 | Polygon -7500403 true true 151 1 185 108 298 108 207 175 242 282 151 216 59 282 94 175 3 108 116 108 624 | 625 | target 626 | false 627 | 0 628 | Circle -7500403 true true 0 0 300 629 | Circle -16777216 true false 30 30 240 630 | Circle -7500403 true true 60 60 180 631 | Circle -16777216 true false 90 90 120 632 | Circle -7500403 true true 120 120 60 633 | 634 | tree 635 | false 636 | 0 637 | Circle -7500403 true true 118 3 94 638 | Rectangle -6459832 true false 120 195 180 300 639 | Circle -7500403 true true 65 21 108 640 | Circle -7500403 true true 116 41 127 641 | Circle -7500403 true true 45 90 120 642 | Circle -7500403 true true 104 74 152 643 | 644 | triangle 645 | false 646 | 0 647 | Polygon -7500403 true true 150 30 15 255 285 255 648 | 649 | triangle 2 650 | false 651 | 0 652 | Polygon -7500403 true true 0 0 0 300 300 300 30 30 653 | 654 | triangle2 655 | false 656 | 0 657 | Polygon -7500403 true true 150 0 0 300 300 300 658 | 659 | truck 660 | false 661 | 0 662 | Rectangle -7500403 true true 4 45 195 187 663 | Polygon -7500403 true true 296 193 296 150 259 134 244 104 208 104 207 194 664 | Rectangle -1 true false 195 60 195 105 665 | Polygon -16777216 true false 238 112 252 141 219 141 218 112 666 | Circle -16777216 true false 234 174 42 667 | Rectangle -7500403 true true 181 185 214 194 668 | Circle -16777216 true false 144 174 42 669 | Circle -16777216 true false 24 174 42 670 | Circle -7500403 false true 24 174 42 671 | Circle -7500403 false true 144 174 42 672 | Circle -7500403 false true 234 174 42 673 | 674 | turtle 675 | true 676 | 0 677 | Polygon -10899396 true false 215 204 240 233 246 254 228 266 215 252 193 210 678 | Polygon -10899396 true false 195 90 225 75 245 75 260 89 269 108 261 124 240 105 225 105 210 105 679 | Polygon -10899396 true false 105 90 75 75 55 75 40 89 31 108 39 124 60 105 75 105 90 105 680 | Polygon -10899396 true false 132 85 134 64 107 51 108 17 150 2 192 18 192 52 169 65 172 87 681 | Polygon -10899396 true false 85 204 60 233 54 254 72 266 85 252 107 210 682 | Polygon -7500403 true true 119 75 179 75 209 101 224 135 220 225 175 261 128 261 81 224 74 135 88 99 683 | 684 | wheel 685 | false 686 | 0 687 | Circle -7500403 true true 3 3 294 688 | Circle -16777216 true false 30 30 240 689 | Line -7500403 true 150 285 150 15 690 | Line -7500403 true 15 150 285 150 691 | Circle -7500403 true true 120 120 60 692 | Line -7500403 true 216 40 79 269 693 | Line -7500403 true 40 84 269 221 694 | Line -7500403 true 40 216 269 79 695 | Line -7500403 true 84 40 221 269 696 | 697 | x 698 | false 699 | 0 700 | Polygon -7500403 true true 300 60 225 0 0 225 60 300 701 | Polygon -7500403 true true 0 60 75 0 300 240 225 300 702 | @#$#@#$#@ 703 | NetLogo 6.3.0 704 | @#$#@#$#@ 705 | @#$#@#$#@ 706 | @#$#@#$#@ 707 | @#$#@#$#@ 708 | @#$#@#$#@ 709 | default 710 | 0.0 711 | -0.2 0 0.0 1.0 712 | 0.0 1 1.0 0.0 713 | 0.2 0 0.0 1.0 714 | link direction 715 | true 716 | 0 717 | Line -7500403 true 150 150 90 180 718 | Line -7500403 true 150 150 210 180 719 | @#$#@#$#@ 720 | 1 721 | @#$#@#$#@ 722 | 723 | -------------------------------------------------------------------------------- /WolfSheep/NetLogo/WolfSheep.nlogo: -------------------------------------------------------------------------------- 1 | extensions [profiler] 2 | 3 | globals [ 4 | max-sheep ; don't let the sheep population grow too large 5 | time-tot 6 | ] 7 | 8 | ; Sheep and wolves are both breeds of turtles 9 | breed [ sheep a-sheep ] ; sheep is its own plural, so we use "a-sheep" as the singular 10 | breed [ wolves wolf ] 11 | 12 | turtles-own [ energy ] ; both wolves and sheep have energy 13 | 14 | patches-own [ countdown ] ; this is for the sheep-wolves-grass model version 15 | 16 | to setup 17 | clear-all 18 | random-seed seed 19 | ifelse netlogo-web? [ set max-sheep 10000 ] [ set max-sheep 30000 ] 20 | 21 | ; Check model-version switch 22 | ; if we're not modeling grass, then the sheep don't need to eat to survive 23 | ; otherwise each grass' state of growth and growing logic need to be set up 24 | ifelse model-version = "sheep-wolves-grass" [ 25 | ask patches [ 26 | set pcolor one-of [ green brown ] 27 | ifelse pcolor = green 28 | [ set countdown grass-regrowth-time ] 29 | [ set countdown random grass-regrowth-time ] ; initialize grass regrowth clocks randomly for brown patches 30 | ] 31 | ] 32 | [ 33 | ask patches [ set pcolor green ] 34 | ] 35 | 36 | create-sheep initial-number-sheep ; create the sheep, then initialize their variables 37 | [ 38 | set shape "sheep" 39 | set color white 40 | set size 1 ; easier to see 41 | set label-color blue - 2 42 | set energy random (2 * sheep-gain-from-food) 43 | setxy random-pxcor random-pycor 44 | ] 45 | 46 | create-wolves initial-number-wolves ; create the wolves, then initialize their variables 47 | [ 48 | set shape "wolf" 49 | set color black 50 | set size 1 ; easier to see 51 | set energy random (2 * wolf-gain-from-food) 52 | setxy random-pxcor random-pycor 53 | ] 54 | display-labels 55 | set time-tot 0 56 | reset-ticks 57 | end 58 | 59 | to benchmark 60 | profiler:reset ;; clear the data 61 | profiler:start ;; start profiling 62 | setup 63 | repeat n_ticks [go] ;; run for n_ticks steps 64 | profiler:stop ;; stop profiling 65 | set time-tot profiler:inclusive-time "go" + profiler:inclusive-time "setup" 66 | file-open "times.txt" 67 | file-print time-tot 68 | file-close 69 | end 70 | 71 | to go 72 | ask sheep [ 73 | move 74 | 75 | ; in this version, sheep eat grass, grass grows, and it costs sheep energy to move 76 | if model-version = "sheep-wolves-grass" [ 77 | set energy energy - 1 ; deduct energy for sheep only if running sheep-wolves-grass model version 78 | eat-grass ; sheep eat grass only if running the sheep-wolves-grass model version 79 | death ; sheep die from starvation only if running the sheep-wolves-grass model version 80 | ] 81 | 82 | reproduce-sheep ; sheep reproduce at a random rate governed by a slider 83 | ] 84 | ask wolves [ 85 | move 86 | set energy energy - 1 ; wolves lose energy as they move 87 | eat-sheep ; wolves eat a sheep on their patch 88 | death ; wolves die if they run out of energy 89 | reproduce-wolves ; wolves reproduce at a random rate governed by a slider 90 | ] 91 | 92 | if model-version = "sheep-wolves-grass" [ ask patches [ grow-grass ] ] 93 | 94 | tick 95 | display-labels 96 | end 97 | 98 | to move ; turtle procedure 99 | rt random 50 100 | lt random 50 101 | fd 1 102 | end 103 | 104 | to eat-grass ; sheep procedure 105 | ; sheep eat grass and turn the patch brown 106 | if pcolor = green [ 107 | set pcolor brown 108 | set energy energy + sheep-gain-from-food ; sheep gain energy by eating 109 | ] 110 | end 111 | 112 | to reproduce-sheep ; sheep procedure 113 | if random-float 100 < sheep-reproduce [ ; throw "dice" to see if you will reproduce 114 | set energy (energy / 2) ; divide energy between parent and offspring 115 | hatch 1 [ rt random-float 360 fd 1 ] ; hatch an offspring and move it forward 1 step 116 | ] 117 | end 118 | 119 | to reproduce-wolves ; wolf procedure 120 | if random-float 100 < wolf-reproduce [ ; throw "dice" to see if you will reproduce 121 | set energy (energy / 2) ; divide energy between parent and offspring 122 | hatch 1 [ rt random-float 360 fd 1 ] ; hatch an offspring and move it forward 1 step 123 | ] 124 | end 125 | 126 | to eat-sheep ; wolf procedure 127 | let prey one-of sheep-here ; grab a random sheep 128 | if prey != nobody [ ; did we get one? if so, 129 | ask prey [ die ] ; kill it, and... 130 | set energy energy + wolf-gain-from-food ; get energy from eating 131 | ] 132 | end 133 | 134 | to death ; turtle procedure (i.e. both wolf and sheep procedure) 135 | ; when energy dips below zero, die 136 | if energy < 0 [ die ] 137 | end 138 | 139 | to grow-grass ; patch procedure 140 | ; countdown on brown patches: if you reach 0, grow some grass 141 | if pcolor = brown [ 142 | ifelse countdown <= 0 143 | [ set pcolor green 144 | set countdown grass-regrowth-time ] 145 | [ set countdown countdown - 1 ] 146 | ] 147 | end 148 | 149 | to-report grass 150 | ifelse model-version = "sheep-wolves-grass" [ 151 | report patches with [pcolor = green] 152 | ] 153 | [ report 0 ] 154 | end 155 | 156 | 157 | to display-labels 158 | ask turtles [ set label "" ] 159 | if show-energy? [ 160 | ask wolves [ set label round energy ] 161 | if model-version = "sheep-wolves-grass" [ ask sheep [ set label round energy ] ] 162 | ] 163 | end 164 | 165 | 166 | ; Copyright 1997 Uri Wilensky. 167 | ; See Info tab for full copyright and license. 168 | @#$#@#$#@ 169 | GRAPHICS-WINDOW 170 | 355 171 | 10 172 | 863 173 | 519 174 | -1 175 | -1 176 | 20.0 177 | 1 178 | 14 179 | 1 180 | 1 181 | 1 182 | 0 183 | 1 184 | 1 185 | 1 186 | 0 187 | 24 188 | 0 189 | 24 190 | 0 191 | 0 192 | 1 193 | ticks 194 | 30.0 195 | 196 | SLIDER 197 | 5 198 | 60 199 | 179 200 | 93 201 | initial-number-sheep 202 | initial-number-sheep 203 | 0 204 | 250 205 | 60.0 206 | 1 207 | 1 208 | NIL 209 | HORIZONTAL 210 | 211 | SLIDER 212 | 5 213 | 196 214 | 179 215 | 229 216 | seed 217 | seed 218 | 0.0 219 | 50000.0 220 | 1.0 221 | 1.0 222 | 1 223 | NIL 224 | HORIZONTAL 225 | 226 | SLIDER 227 | 5 228 | 196 229 | 179 230 | 229 231 | n_ticks 232 | n_ticks 233 | 0.0 234 | 50000.0 235 | 1.0 236 | 1.0 237 | 1 238 | NIL 239 | HORIZONTAL 240 | 241 | SLIDER 242 | 5 243 | 196 244 | 179 245 | 229 246 | sheep-gain-from-food 247 | sheep-gain-from-food 248 | 0.0 249 | 50.0 250 | 5.0 251 | 1.0 252 | 1 253 | NIL 254 | HORIZONTAL 255 | 256 | SLIDER 257 | 5 258 | 231 259 | 179 260 | 264 261 | sheep-reproduce 262 | sheep-reproduce 263 | 1.0 264 | 20.0 265 | 20.0 266 | 1.0 267 | 1 268 | % 269 | HORIZONTAL 270 | 271 | SLIDER 272 | 185 273 | 60 274 | 350 275 | 93 276 | initial-number-wolves 277 | initial-number-wolves 278 | 0 279 | 250 280 | 40.0 281 | 1 282 | 1 283 | NIL 284 | HORIZONTAL 285 | 286 | SLIDER 287 | 183 288 | 195 289 | 348 290 | 228 291 | wolf-gain-from-food 292 | wolf-gain-from-food 293 | 0.0 294 | 100.0 295 | 13.0 296 | 1.0 297 | 1 298 | NIL 299 | HORIZONTAL 300 | 301 | SLIDER 302 | 183 303 | 231 304 | 348 305 | 264 306 | wolf-reproduce 307 | wolf-reproduce 308 | 0.0 309 | 20.0 310 | 10.0 311 | 1.0 312 | 1 313 | % 314 | HORIZONTAL 315 | 316 | SLIDER 317 | 40 318 | 100 319 | 252 320 | 133 321 | grass-regrowth-time 322 | grass-regrowth-time 323 | 0 324 | 100 325 | 20.0 326 | 1 327 | 1 328 | NIL 329 | HORIZONTAL 330 | 331 | BUTTON 332 | 40 333 | 140 334 | 109 335 | 173 336 | setup 337 | setup 338 | NIL 339 | 1 340 | T 341 | OBSERVER 342 | NIL 343 | NIL 344 | NIL 345 | NIL 346 | 1 347 | 348 | BUTTON 349 | 115 350 | 140 351 | 190 352 | 173 353 | go 354 | go 355 | T 356 | 1 357 | T 358 | OBSERVER 359 | NIL 360 | NIL 361 | NIL 362 | NIL 363 | 0 364 | 365 | TEXTBOX 366 | 20 367 | 178 368 | 160 369 | 196 370 | Sheep settings 371 | 11 372 | 0.0 373 | 0 374 | 375 | TEXTBOX 376 | 198 377 | 176 378 | 311 379 | 194 380 | Wolf settings 381 | 11 382 | 0.0 383 | 0 384 | 385 | SWITCH 386 | 105 387 | 270 388 | 241 389 | 303 390 | show-energy? 391 | show-energy? 392 | 1 393 | 1 394 | -1000 395 | 396 | CHOOSER 397 | 5 398 | 10 399 | 350 400 | 55 401 | model-version 402 | model-version 403 | "sheep-wolves" "sheep-wolves-grass" 404 | 1 405 | 406 | BUTTON 407 | 210 408 | 145 409 | 322 410 | 178 411 | benchmark 412 | benchmark 413 | NIL 414 | 1 415 | T 416 | OBSERVER 417 | NIL 418 | NIL 419 | NIL 420 | NIL 421 | 1 422 | 423 | PLOT 424 | 30 425 | 380 426 | 330 427 | 615 428 | plot 1 429 | NIL 430 | NIL 431 | 0.0 432 | 10.0 433 | 0.0 434 | 10.0 435 | true 436 | false 437 | "" "" 438 | PENS 439 | "default" 1.0 0 -2674135 true "" "plot count wolves" 440 | "pen-1" 1.0 0 -13345367 true "" "plot count sheep" 441 | "pen-2" 1.0 0 -10899396 true "" "plot count grass" 442 | 443 | @#$#@#$#@ 444 | ## WHAT IS IT? 445 | 446 | This model explores the stability of predator-prey ecosystems. Such a system is called unstable if it tends to result in extinction for one or more species involved. In contrast, a system is stable if it tends to maintain itself over time, despite fluctuations in population sizes. 447 | 448 | ## HOW IT WORKS 449 | 450 | There are two main variations to this model. 451 | 452 | In the first variation, the "sheep-wolves" version, wolves and sheep wander randomly around the landscape, while the wolves look for sheep to prey on. Each step costs the wolves energy, and they must eat sheep in order to replenish their energy - when they run out of energy they die. To allow the population to continue, each wolf or sheep has a fixed probability of reproducing at each time step. In this variation, we model the grass as "infinite" so that sheep always have enough to eat, and we don't explicitly model the eating or growing of grass. As such, sheep don't either gain or lose energy by eating or moving. This variation produces interesting population dynamics, but is ultimately unstable. This variation of the model is particularly well-suited to interacting species in a rich nutrient environment, such as two strains of bacteria in a petri dish (Gause, 1934). 453 | 454 | The second variation, the "sheep-wolves-grass" version explictly models grass (green) in addition to wolves and sheep. The behavior of the wolves is identical to the first variation, however this time the sheep must eat grass in order to maintain their energy - when they run out of energy they die. Once grass is eaten it will only regrow after a fixed amount of time. This variation is more complex than the first, but it is generally stable. It is a closer match to the classic Lotka Volterra population oscillation models. The classic LV models though assume the populations can take on real values, but in small populations these models underestimate extinctions and agent-based models such as the ones here, provide more realistic results. (See Wilensky & Rand, 2015; chapter 4). 455 | 456 | The construction of this model is described in two papers by Wilensky & Reisman (1998; 2006) referenced below. 457 | 458 | ## HOW TO USE IT 459 | 460 | 1. Set the model-version chooser to "sheep-wolves-grass" to include grass eating and growth in the model, or to "sheep-wolves" to only include wolves (black) and sheep (white). 461 | 2. Adjust the slider parameters (see below), or use the default settings. 462 | 3. Press the SETUP button. 463 | 4. Press the GO button to begin the simulation. 464 | 5. Look at the monitors to see the current population sizes 465 | 6. Look at the POPULATIONS plot to watch the populations fluctuate over time 466 | 467 | Parameters: 468 | MODEL-VERSION: Whether we model sheep wolves and grass or just sheep and wolves 469 | INITIAL-NUMBER-SHEEP: The initial size of sheep population 470 | INITIAL-NUMBER-WOLVES: The initial size of wolf population 471 | SHEEP-GAIN-FROM-FOOD: The amount of energy sheep get for every grass patch eaten (Note this is not used in the sheep-wolves model version) 472 | WOLF-GAIN-FROM-FOOD: The amount of energy wolves get for every sheep eaten 473 | SHEEP-REPRODUCE: The probability of a sheep reproducing at each time step 474 | WOLF-REPRODUCE: The probability of a wolf reproducing at each time step 475 | GRASS-REGROWTH-TIME: How long it takes for grass to regrow once it is eaten (Note this is not used in the sheep-wolves model version) 476 | SHOW-ENERGY?: Whether or not to show the energy of each animal as a number 477 | 478 | Notes: 479 | - one unit of energy is deducted for every step a wolf takes 480 | - when running the sheep-wolves-grass model version, one unit of energy is deducted for every step a sheep takes 481 | 482 | There are three monitors to show the populations of the wolves, sheep and grass and a populations plot to display the population values over time. 483 | 484 | If there are no wolves left and too many sheep, the model run stops. 485 | 486 | ## THINGS TO NOTICE 487 | 488 | When running the sheep-wolves model variation, watch as the sheep and wolf populations fluctuate. Notice that increases and decreases in the sizes of each population are related. In what way are they related? What eventually happens? 489 | 490 | In the sheep-wolves-grass model variation, notice the green line added to the population plot representing fluctuations in the amount of grass. How do the sizes of the three populations appear to relate now? What is the explanation for this? 491 | 492 | Why do you suppose that some variations of the model might be stable while others are not? 493 | 494 | ## THINGS TO TRY 495 | 496 | Try adjusting the parameters under various settings. How sensitive is the stability of the model to the particular parameters? 497 | 498 | Can you find any parameters that generate a stable ecosystem in the sheep-wolves model variation? 499 | 500 | Try running the sheep-wolves-grass model variation, but setting INITIAL-NUMBER-WOLVES to 0. This gives a stable ecosystem with only sheep and grass. Why might this be stable while the variation with only sheep and wolves is not? 501 | 502 | Notice that under stable settings, the populations tend to fluctuate at a predictable pace. Can you find any parameters that will speed this up or slow it down? 503 | 504 | ## EXTENDING THE MODEL 505 | 506 | There are a number ways to alter the model so that it will be stable with only wolves and sheep (no grass). Some will require new elements to be coded in or existing behaviors to be changed. Can you develop such a version? 507 | 508 | Try changing the reproduction rules -- for example, what would happen if reproduction depended on energy rather than being determined by a fixed probability? 509 | 510 | Can you modify the model so the sheep will flock? 511 | 512 | Can you modify the model so that wolves actively chase sheep? 513 | 514 | ## NETLOGO FEATURES 515 | 516 | Note the use of breeds to model two different kinds of "turtles": wolves and sheep. Note the use of patches to model grass. 517 | 518 | Note use of the ONE-OF agentset reporter to select a random sheep to be eaten by a wolf. 519 | 520 | ## RELATED MODELS 521 | 522 | Look at Rabbits Grass Weeds for another model of interacting populations with different rules. 523 | 524 | ## CREDITS AND REFERENCES 525 | 526 | Wilensky, U. & Reisman, K. (1998). Connected Science: Learning Biology through Constructing and Testing Computational Theories -- an Embodied Modeling Approach. International Journal of Complex Systems, M. 234, pp. 1 - 12. (The Wolf-Sheep-Predation model is a slightly extended version of the model described in the paper.) 527 | 528 | Wilensky, U. & Reisman, K. (2006). Thinking like a Wolf, a Sheep or a Firefly: Learning Biology through Constructing and Testing Computational Theories -- an Embodied Modeling Approach. Cognition & Instruction, 24(2), pp. 171-209. http://ccl.northwestern.edu/papers/wolfsheep.pdf . 529 | 530 | Wilensky, U., & Rand, W. (2015). An introduction to agent-based modeling: Modeling natural, social and engineered complex systems with NetLogo. Cambridge, MA: MIT Press. 531 | 532 | Lotka, A. J. (1925). Elements of physical biology. New York: Dover. 533 | 534 | Volterra, V. (1926, October 16). Fluctuations in the abundance of a species considered mathematically. Nature, 118, 558–560. 535 | 536 | Gause, G. F. (1934). The struggle for existence. Baltimore: Williams & Wilkins. 537 | 538 | ## HOW TO CITE 539 | 540 | If you mention this model or the NetLogo software in a publication, we ask that you include the citations below. 541 | 542 | For the model itself: 543 | 544 | * Wilensky, U. (1997). NetLogo Wolf Sheep Predation model. http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 545 | 546 | Please cite the NetLogo software as: 547 | 548 | * Wilensky, U. (1999). NetLogo. http://ccl.northwestern.edu/netlogo/. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. 549 | 550 | ## COPYRIGHT AND LICENSE 551 | 552 | Copyright 1997 Uri Wilensky. 553 | 554 | ![CC BY-NC-SA 3.0](http://ccl.northwestern.edu/images/creativecommons/byncsa.png) 555 | 556 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 557 | 558 | Commercial licenses are also available. To inquire about commercial licenses, please contact Uri Wilensky at uri@northwestern.edu. 559 | 560 | This model was created as part of the project: CONNECTED MATHEMATICS: MAKING SENSE OF COMPLEX PHENOMENA THROUGH BUILDING OBJECT-BASED PARALLEL MODELS (OBPML). The project gratefully acknowledges the support of the National Science Foundation (Applications of Advanced Technologies Program) -- grant numbers RED #9552950 and REC #9632612. 561 | 562 | This model was converted to NetLogo as part of the projects: PARTICIPATORY SIMULATIONS: NETWORK-BASED DESIGN FOR SYSTEMS LEARNING IN CLASSROOMS and/or INTEGRATED SIMULATION AND MODELING ENVIRONMENT. The project gratefully acknowledges the support of the National Science Foundation (REPP & ROLE programs) -- grant numbers REC #9814682 and REC-0126227. Converted from StarLogoT to NetLogo, 2000. 563 | 564 | 565 | @#$#@#$#@ 566 | default 567 | true 568 | 0 569 | Polygon -7500403 true true 150 5 40 250 150 205 260 250 570 | 571 | airplane 572 | true 573 | 0 574 | Polygon -7500403 true true 150 0 135 15 120 60 120 105 15 165 15 195 120 180 135 240 105 270 120 285 150 270 180 285 210 270 165 240 180 180 285 195 285 165 180 105 180 60 165 15 575 | 576 | arrow 577 | true 578 | 0 579 | Polygon -7500403 true true 150 0 0 150 105 150 105 293 195 293 195 150 300 150 580 | 581 | box 582 | true 583 | 0 584 | Polygon -7500403 true true 150 285 285 225 285 75 150 135 585 | Polygon -7500403 true true 150 135 15 75 150 15 285 75 586 | Polygon -7500403 true true 15 75 15 225 150 285 150 135 587 | Line -16777216 false 150 285 150 135 588 | Line -16777216 false 150 135 15 75 589 | Line -16777216 false 150 135 285 75 590 | 591 | bug 592 | true 593 | 0 594 | Circle -7500403 true true 96 182 108 595 | Circle -7500403 true true 110 127 80 596 | Circle -7500403 true true 110 75 80 597 | Line -7500403 true 150 100 80 30 598 | Line -7500403 true 150 100 220 30 599 | 600 | butterfly 601 | true 602 | 0 603 | Polygon -7500403 true true 150 165 209 199 225 225 225 255 195 270 165 255 150 240 604 | Polygon -7500403 true true 150 165 89 198 75 225 75 255 105 270 135 255 150 240 605 | Polygon -7500403 true true 139 148 100 105 55 90 25 90 10 105 10 135 25 180 40 195 85 194 139 163 606 | Polygon -7500403 true true 162 150 200 105 245 90 275 90 290 105 290 135 275 180 260 195 215 195 162 165 607 | Polygon -16777216 true false 150 255 135 225 120 150 135 120 150 105 165 120 180 150 165 225 608 | Circle -16777216 true false 135 90 30 609 | Line -16777216 false 150 105 195 60 610 | Line -16777216 false 150 105 105 60 611 | 612 | car 613 | false 614 | 0 615 | Polygon -7500403 true true 300 180 279 164 261 144 240 135 226 132 213 106 203 84 185 63 159 50 135 50 75 60 0 150 0 165 0 225 300 225 300 180 616 | Circle -16777216 true false 180 180 90 617 | Circle -16777216 true false 30 180 90 618 | Polygon -16777216 true false 162 80 132 78 134 135 209 135 194 105 189 96 180 89 619 | Circle -7500403 true true 47 195 58 620 | Circle -7500403 true true 195 195 58 621 | 622 | circle 623 | false 624 | 0 625 | Circle -7500403 true true 0 0 300 626 | 627 | circle 2 628 | false 629 | 0 630 | Circle -7500403 true true 0 0 300 631 | Circle -16777216 true false 30 30 240 632 | 633 | cow 634 | false 635 | 0 636 | Polygon -7500403 true true 200 193 197 249 179 249 177 196 166 187 140 189 93 191 78 179 72 211 49 209 48 181 37 149 25 120 25 89 45 72 103 84 179 75 198 76 252 64 272 81 293 103 285 121 255 121 242 118 224 167 637 | Polygon -7500403 true true 73 210 86 251 62 249 48 208 638 | Polygon -7500403 true true 25 114 16 195 9 204 23 213 25 200 39 123 639 | 640 | cylinder 641 | false 642 | 0 643 | Circle -7500403 true true 0 0 300 644 | 645 | dot 646 | false 647 | 0 648 | Circle -7500403 true true 90 90 120 649 | 650 | face happy 651 | false 652 | 0 653 | Circle -7500403 true true 8 8 285 654 | Circle -16777216 true false 60 75 60 655 | Circle -16777216 true false 180 75 60 656 | Polygon -16777216 true false 150 255 90 239 62 213 47 191 67 179 90 203 109 218 150 225 192 218 210 203 227 181 251 194 236 217 212 240 657 | 658 | face neutral 659 | false 660 | 0 661 | Circle -7500403 true true 8 7 285 662 | Circle -16777216 true false 60 75 60 663 | Circle -16777216 true false 180 75 60 664 | Rectangle -16777216 true false 60 195 240 225 665 | 666 | face sad 667 | false 668 | 0 669 | Circle -7500403 true true 8 8 285 670 | Circle -16777216 true false 60 75 60 671 | Circle -16777216 true false 180 75 60 672 | Polygon -16777216 true false 150 168 90 184 62 210 47 232 67 244 90 220 109 205 150 198 192 205 210 220 227 242 251 229 236 206 212 183 673 | 674 | fish 675 | false 676 | 0 677 | Polygon -1 true false 44 131 21 87 15 86 0 120 15 150 0 180 13 214 20 212 45 166 678 | Polygon -1 true false 135 195 119 235 95 218 76 210 46 204 60 165 679 | Polygon -1 true false 75 45 83 77 71 103 86 114 166 78 135 60 680 | Polygon -7500403 true true 30 136 151 77 226 81 280 119 292 146 292 160 287 170 270 195 195 210 151 212 30 166 681 | Circle -16777216 true false 215 106 30 682 | 683 | flag 684 | false 685 | 0 686 | Rectangle -7500403 true true 60 15 75 300 687 | Polygon -7500403 true true 90 150 270 90 90 30 688 | Line -7500403 true 75 135 90 135 689 | Line -7500403 true 75 45 90 45 690 | 691 | flower 692 | false 693 | 0 694 | Polygon -10899396 true false 135 120 165 165 180 210 180 240 150 300 165 300 195 240 195 195 165 135 695 | Circle -7500403 true true 85 132 38 696 | Circle -7500403 true true 130 147 38 697 | Circle -7500403 true true 192 85 38 698 | Circle -7500403 true true 85 40 38 699 | Circle -7500403 true true 177 40 38 700 | Circle -7500403 true true 177 132 38 701 | Circle -7500403 true true 70 85 38 702 | Circle -7500403 true true 130 25 38 703 | Circle -7500403 true true 96 51 108 704 | Circle -16777216 true false 113 68 74 705 | Polygon -10899396 true false 189 233 219 188 249 173 279 188 234 218 706 | Polygon -10899396 true false 180 255 150 210 105 210 75 240 135 240 707 | 708 | house 709 | false 710 | 0 711 | Rectangle -7500403 true true 45 120 255 285 712 | Rectangle -16777216 true false 120 210 180 285 713 | Polygon -7500403 true true 15 120 150 15 285 120 714 | Line -16777216 false 30 120 270 120 715 | 716 | leaf 717 | false 718 | 0 719 | Polygon -7500403 true true 150 210 135 195 120 210 60 210 30 195 60 180 60 165 15 135 30 120 15 105 40 104 45 90 60 90 90 105 105 120 120 120 105 60 120 60 135 30 150 15 165 30 180 60 195 60 180 120 195 120 210 105 240 90 255 90 263 104 285 105 270 120 285 135 240 165 240 180 270 195 240 210 180 210 165 195 720 | Polygon -7500403 true true 135 195 135 240 120 255 105 255 105 285 135 285 165 240 165 195 721 | 722 | line 723 | true 724 | 0 725 | Line -7500403 true 150 0 150 300 726 | 727 | line half 728 | true 729 | 0 730 | Line -7500403 true 150 0 150 150 731 | 732 | pentagon 733 | false 734 | 0 735 | Polygon -7500403 true true 150 15 15 120 60 285 240 285 285 120 736 | 737 | person 738 | false 739 | 0 740 | Circle -7500403 true true 110 5 80 741 | Polygon -7500403 true true 105 90 120 195 90 285 105 300 135 300 150 225 165 300 195 300 210 285 180 195 195 90 742 | Rectangle -7500403 true true 127 79 172 94 743 | Polygon -7500403 true true 195 90 240 150 225 180 165 105 744 | Polygon -7500403 true true 105 90 60 150 75 180 135 105 745 | 746 | plant 747 | false 748 | 0 749 | Rectangle -7500403 true true 135 90 165 300 750 | Polygon -7500403 true true 135 255 90 210 45 195 75 255 135 285 751 | Polygon -7500403 true true 165 255 210 210 255 195 225 255 165 285 752 | Polygon -7500403 true true 135 180 90 135 45 120 75 180 135 210 753 | Polygon -7500403 true true 165 180 165 210 225 180 255 120 210 135 754 | Polygon -7500403 true true 135 105 90 60 45 45 75 105 135 135 755 | Polygon -7500403 true true 165 105 165 135 225 105 255 45 210 60 756 | Polygon -7500403 true true 135 90 120 45 150 15 180 45 165 90 757 | 758 | sheep 759 | false 760 | 15 761 | Circle -1 true true 203 65 88 762 | Circle -1 true true 70 65 162 763 | Circle -1 true true 150 105 120 764 | Polygon -7500403 true false 218 120 240 165 255 165 278 120 765 | Circle -7500403 true false 214 72 67 766 | Rectangle -1 true true 164 223 179 298 767 | Polygon -1 true true 45 285 30 285 30 240 15 195 45 210 768 | Circle -1 true true 3 83 150 769 | Rectangle -1 true true 65 221 80 296 770 | Polygon -1 true true 195 285 210 285 210 240 240 210 195 210 771 | Polygon -7500403 true false 276 85 285 105 302 99 294 83 772 | Polygon -7500403 true false 219 85 210 105 193 99 201 83 773 | 774 | square 775 | false 776 | 0 777 | Rectangle -7500403 true true 30 30 270 270 778 | 779 | square 2 780 | false 781 | 0 782 | Rectangle -7500403 true true 30 30 270 270 783 | Rectangle -16777216 true false 60 60 240 240 784 | 785 | star 786 | false 787 | 0 788 | Polygon -7500403 true true 151 1 185 108 298 108 207 175 242 282 151 216 59 282 94 175 3 108 116 108 789 | 790 | target 791 | false 792 | 0 793 | Circle -7500403 true true 0 0 300 794 | Circle -16777216 true false 30 30 240 795 | Circle -7500403 true true 60 60 180 796 | Circle -16777216 true false 90 90 120 797 | Circle -7500403 true true 120 120 60 798 | 799 | tree 800 | false 801 | 0 802 | Circle -7500403 true true 118 3 94 803 | Rectangle -6459832 true false 120 195 180 300 804 | Circle -7500403 true true 65 21 108 805 | Circle -7500403 true true 116 41 127 806 | Circle -7500403 true true 45 90 120 807 | Circle -7500403 true true 104 74 152 808 | 809 | triangle 810 | false 811 | 0 812 | Polygon -7500403 true true 150 30 15 255 285 255 813 | 814 | triangle 2 815 | false 816 | 0 817 | Polygon -7500403 true true 150 30 15 255 285 255 818 | Polygon -16777216 true false 151 99 225 223 75 224 819 | 820 | truck 821 | false 822 | 0 823 | Rectangle -7500403 true true 4 45 195 187 824 | Polygon -7500403 true true 296 193 296 150 259 134 244 104 208 104 207 194 825 | Rectangle -1 true false 195 60 195 105 826 | Polygon -16777216 true false 238 112 252 141 219 141 218 112 827 | Circle -16777216 true false 234 174 42 828 | Rectangle -7500403 true true 181 185 214 194 829 | Circle -16777216 true false 144 174 42 830 | Circle -16777216 true false 24 174 42 831 | Circle -7500403 false true 24 174 42 832 | Circle -7500403 false true 144 174 42 833 | Circle -7500403 false true 234 174 42 834 | 835 | turtle 836 | true 837 | 0 838 | Polygon -10899396 true false 215 204 240 233 246 254 228 266 215 252 193 210 839 | Polygon -10899396 true false 195 90 225 75 245 75 260 89 269 108 261 124 240 105 225 105 210 105 840 | Polygon -10899396 true false 105 90 75 75 55 75 40 89 31 108 39 124 60 105 75 105 90 105 841 | Polygon -10899396 true false 132 85 134 64 107 51 108 17 150 2 192 18 192 52 169 65 172 87 842 | Polygon -10899396 true false 85 204 60 233 54 254 72 266 85 252 107 210 843 | Polygon -7500403 true true 119 75 179 75 209 101 224 135 220 225 175 261 128 261 81 224 74 135 88 99 844 | 845 | wheel 846 | false 847 | 0 848 | Circle -7500403 true true 3 3 294 849 | Circle -16777216 true false 30 30 240 850 | Line -7500403 true 150 285 150 15 851 | Line -7500403 true 15 150 285 150 852 | Circle -7500403 true true 120 120 60 853 | Line -7500403 true 216 40 79 269 854 | Line -7500403 true 40 84 269 221 855 | Line -7500403 true 40 216 269 79 856 | Line -7500403 true 84 40 221 269 857 | 858 | wolf 859 | false 860 | 0 861 | Polygon -16777216 true false 253 133 245 131 245 133 862 | Polygon -7500403 true true 2 194 13 197 30 191 38 193 38 205 20 226 20 257 27 265 38 266 40 260 31 253 31 230 60 206 68 198 75 209 66 228 65 243 82 261 84 268 100 267 103 261 77 239 79 231 100 207 98 196 119 201 143 202 160 195 166 210 172 213 173 238 167 251 160 248 154 265 169 264 178 247 186 240 198 260 200 271 217 271 219 262 207 258 195 230 192 198 210 184 227 164 242 144 259 145 284 151 277 141 293 140 299 134 297 127 273 119 270 105 863 | Polygon -7500403 true true -1 195 14 180 36 166 40 153 53 140 82 131 134 133 159 126 188 115 227 108 236 102 238 98 268 86 269 92 281 87 269 103 269 113 864 | 865 | x 866 | false 867 | 0 868 | Polygon -7500403 true true 270 75 225 30 30 225 75 270 869 | Polygon -7500403 true true 30 75 75 30 270 225 225 270 870 | @#$#@#$#@ 871 | NetLogo 6.1.1 872 | @#$#@#$#@ 873 | set model-version "sheep-wolves-grass" 874 | set show-energy? false 875 | setup 876 | repeat 75 [ go ] 877 | @#$#@#$#@ 878 | @#$#@#$#@ 879 | @#$#@#$#@ 880 | @#$#@#$#@ 881 | default 882 | 0.0 883 | -0.2 0 0.0 1.0 884 | 0.0 1 1.0 0.0 885 | 0.2 0 0.0 1.0 886 | link direction 887 | true 888 | 0 889 | Line -7500403 true 150 150 90 180 890 | Line -7500403 true 150 150 210 180 891 | @#$#@#$#@ 892 | 1 893 | @#$#@#$#@ 894 | --------------------------------------------------------------------------------