diff --git a/README.md b/README.md index 0270035..0c9a48c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,9 @@ Fell free to use our code, and if you do so, please cite us! * `plots/` this directory doesn't exist in the repository, but after running the simulation (or a plotting function) it will be created and plots will be generated and saved here by default * `results/` this directory doesn't exist in the repository, but after running the simulation it will be created and results will be saved here by default * `scripts/` different scripts for custom tasks, mainly for running `main.py` many times with different parameters + * `animation.py` a script for making animations of the network showing how states/votes are changing * `binom_approx.py` this script requires to run `main.py` manually with the same parameters first, then on top of the results of the simulation plots a binomial approximation, where voters basically flip a coin to chose their state/vote + * `fit_planar_c.py` a script fitting the `planar_c` parameter value to the commuting data * `media_susceptibility.py` this script runs `main.py` for a range of different mass media influence and plots media susceptibility and other measures * `media_vs_zealots.py` this script runs `main.py` for a range of different numbers of zealots and different mass media influence and plots the results for cross-influenced system * `zealot_susceptibility.py` this script runs `main.py` for a range of different numbers of zealots and plots zealot susceptibility and other measures diff --git a/scripts/animation.py b/scripts/animation.py new file mode 100644 index 0000000..269a8e9 --- /dev/null +++ b/scripts/animation.py @@ -0,0 +1,78 @@ +""" +A script running the simulation for a given configuration (you can provide a config file), +saving plots of the graph on the way, and creating an animation at the end. +This script doesn't have many options as it is meant to play around with it, +so if you want to get some fancy plots etc. just modify the code. +""" +import os +import sys +import glob +import inspect + +# path hack for imports to work when running this script from any location, +# without the hack one has to manually edit PYTHONPATH every time +currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parentdir = os.path.dirname(currentdir) +sys.path.insert(0, parentdir) + +from configuration.parser import get_arguments +from configuration.logging import log +from tools import run_with_time, compute_edge_ratio +from plotting import plot_traj, plot_network +from net_generation.base import init_graph, add_zealots +from simulation.base import run_simulation, run_thermalization + + +NUM_FRAMES = 100 # the number of frames to save +MC_STEP = 10 # the number of Monte Carlo steps of simulation between frames + + +@run_with_time +def make_animation(config): + os.makedirs('plots/animation', exist_ok=True) + os.chdir('plots/animation') + + # initialize the graph + graph = init_graph(config.n, config.district_sizes, config.avg_deg, block_coords=config.district_coords, + ratio=config.ratio, planar_const=config.planar_c, euclidean=config.euclidean, + state_generator=config.initialize_states, random_dist=config.random_dist, + initial_state=config.not_zealot_state, all_states=config.all_states) + graph = add_zealots(graph, config.n_zealots, zealot_state=config.zealot_state, **config.zealots_config) + + link_fraction, link_ratio = compute_edge_ratio(graph) + log.info(f'There is {str(round(100.0 * link_fraction, 1))}% of inter-district connections') + log.info(f'Ratio of inter- to intra-district links is equal {str(round(link_ratio, 3))}') + + # save the layout to use the same one in each frame + ll = graph.layout() + + # show the first plot with colored districts so one can stop the script and rerun it, if the layout is not nice + plot_network(graph, config, mode='districts', ig_layout=ll) + + if config.therm_time > 1: + graph, trajectory = run_thermalization(config, graph, config.epsilon, config.therm_time, n=config.n) + plot_traj(trajectory, config.suffix) + plot_network(graph, config, mode='states', ig_layout=ll, save_as=f'{0:05d}.png') + + for i in range(NUM_FRAMES): + log.info(f'Running loop no. {i}') + graph = run_simulation(config, graph, config.epsilon, MC_STEP * config.n, n=config.n) + plot_network(graph, config, mode='states', ig_layout=ll, save_as=f'{i + 1:05d}.png') + + # change the command in this line to create the animation with your preferred program and in the preferred format + # for example, you might also use 'convert -delay 10 -loop 0 %05d.png animation.gif' etc. + os.system('ffmpeg -framerate 10 -i %05d.png animation.mp4') + + # don't remove .png files if you want to be able to play around + # with animation parameters (like speed) without having to run the whole simulation again + response = input('Do you want to remove the created .png files? [Y/N] ') + if response == 'Y': + log.info(f'Removing the .png files') + for f in glob.glob('[0-9]' * 5 + '.png'): + os.remove(f) + + +if __name__ == '__main__': + os.chdir(parentdir) + cfg = get_arguments() + make_animation(cfg) diff --git a/scripts/fit_planar_c.py b/scripts/fit_planar_c.py index 38b727e..f274633 100644 --- a/scripts/fit_planar_c.py +++ b/scripts/fit_planar_c.py @@ -1,4 +1,11 @@ # -*- coding: utf-8 -*- +""" +A script fitting the planar_c parameter of the main simulation to commuting data. +The data must be provided as in the examples below in an array, where the first row +contains commuting distances in km, and the second row provides a fraction of people +commuting up to the distance given on the same position in the first row, but more +than the previous distance. The second row must add up to 1. +""" import numpy as np import matplotlib.pyplot as plt from scipy.optimize import minimize