/**
* Name:  Model using Batch mode
* Author:  Patrick Taillandier
* Description: A model showing how to use batch experiments to find the best combination of parameters to minimize the numbers of infected people 
*      in a SIR infection model where agents infect others and become immune after a certain time and has a probability to die. The batch mode uses five different methods : Exhaustive, Hill climbing
*      GA, PSO, Tabu Search, Reactive Tabu Search, Simulated Annealing, and Explicit exploration. The model proposes five experiments : one simple with a User Interface, one running 10 experiments and saving the data, and one 
*      for each strategy. 
* Tags: batch, algorithm, save_file
*/


model batch_example

global {
    int number_people <- 100;  // The init number of people
    int number_I <- 5 ;	// The init number of infected
    float infection_rate <- 0.1 ; // The infection rate
	float infection_distance <- 5.0 ; // infection distance (in meters)
	float dying_proba <- 0.01; //probability to die at each step if infected
	int immune_step <- 100 ; // number of steps before becoming immune after infection
	float speed_people <- 5.0 ; // speed of the Host

	int num_dead <- 0;
	init {
		float t <- machine_time;
		create people number: number_people ;
        ask (number_I among people) {
        	is_infected <- true;
        	color <- #red;
        }
	} 
	
	reflex write_info when: time = 5000  {
		write sample(infection_rate) + " " + sample(dying_proba) + " " +sample(seed) + " -> " + sample(num_dead);
	}
}

species people skills:[moving] {
	bool is_infected <- false;
	bool is_immune <- false;
	rgb color <- #green;
	int cpt <- 0;
	reflex basic_move {
		do wander speed: speed_people;
	}

	reflex die when: is_infected {
		if flip(dying_proba) {
			num_dead <- num_dead + 1;
			do die;
		}
	}
	reflex become_immune when: is_infected {
		if (cpt > immune_step) {
			cpt <- 0;
			is_immune <- true;
			is_infected <- false;
			color <-  #blue;
		} else {
			cpt <- cpt + 1;
		}
	}
	reflex become_infected when: not is_infected and not is_immune{
		if (flip(infection_rate) and not empty(people at_distance infection_distance where each.is_infected)) {
			is_infected <- true;
			color <-  #red;
		}
	}

	aspect default { 
		draw circle(1) color: color;
    }
}

experiment Simple type:gui {
	parameter 'Infection Rate:' var: infection_rate;
	parameter 'Infection Distance:' var: infection_distance;
	output {
		monitor "nb of dead" value: num_dead;
		display map {
			species people aspect: default;
		}
	}
}


// This experiment runs the simulation 5 times.
// At the end of each simulation, the people agents are saved in a shapefile
experiment 'Run 5 simulations' type: batch repeat: 5 keep_seed: true until: ( time > 5000 ) {
	
	// the reflex will be activated at the end of each run; in this experiment a run consists of the execution of 5 simulations (repeat: 5)
	reflex end_of_runs
	{
		int cpt <- 0;
		// each simulation of the run is an agent; it is possible to access to the list of these agents by using the variable "simulations" of the experiment. 
		// Another way of accessing to the simulations consists in using the name of model + _model: here "batch_example_model"
		//in this example, we ask all the simulation agents of the run to save (at the end of the simulation) the people population in a shapefile with their is_infected and is_immune attributes 
		ask simulations
		{
			save people type: "shp" to: "result/people_shape" + cpt + ".shp" attributes: ["INFECTED"::is_infected, "IMMUNE"::is_immune];
			cpt <- cpt + 1;
		}
	}
}

// This experiment explores two parameters with an exhaustive strategy,
// repeating each simulation three times (the aggregated fitness correspond to the mean fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Exhaustive type: batch repeat: 3 keep_seed: true until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate among: [ 0.1 , 0.5 , 1.0 ];
	parameter 'Probability of dying:' var: dying_proba min: 0.1 max: 0.2 step:0.05;
	method exhaustive;
	
	//the permanent section allows to define a output section that will be kept during all the batch experiment
	permanent {
		display Comparison {
			chart "Number of people infected" type: series {
				//we can access to all the simulations of a run (here composed of 5 simulation -> repeat: 5) by the variable "simulations" of the experiment.
				//here we display for the 5 simulations, the mean, min and max values of the nb_infected variable.
				data "Mean" value: mean(simulations collect each.num_dead ) style: spline color: #blue ;
				data "Min" value:  min(simulations collect each.num_dead ) style: spline color: #darkgreen ;
				data "Max" value:  max(simulations collect each.num_dead ) style: spline color: #red ;
			}
		}	
	}
}

// This experiment tests two explicit parameters sets,
// repeating each simulation three times (the aggregated fitness correspond to the mean fitness), 
experiment Explicit type: batch repeat: 3 keep_seed: true until: ( time > 5000 ) {
	method explicit parameter_sets: [
		["infection_rate"::0.1, "dying_proba":: 0.01],
		["infection_rate"::0.5, "dying_proba":: 0.2],
		["infection_rate"::1.0, "dying_proba":: 0.05],
		["infection_rate"::0.5, "dying_proba":: 0.1]
	];
}

// This experiment samples from the parameter space (Saltelli methods) to establish
// Sobol index, i.e. a contribution value of each parameters to 
// observed variance for outcomes of interest, 
// more on this see https://www.jasss.org/19/1/5.html and http://moeaframework.org for the API
experiment Sobol type: batch keep_seed:true until:( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min:0.0 max:1.0;
	parameter 'Probability of dying' var:dying_proba min:0.01 max:0.25;
	method sobol outputs:["num_dead"] sample:100 report:"Results/sobol.txt" results:"Results/sobol_raw.csv";
}

// This experiment explores two parameters with a PSO strategy,
// repeating each simulation three times (the aggregated fitness correspond to the mean fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment PSO type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.01;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method pso num_particles: 3 weight_inertia:0.7 weight_cognitive: 1.5 weight_social: 1.5  iter_max: 5  minimize: num_dead  ; 
}

// This experiment explores two parameters with a GA strategy,
// repeating each simulation three times (the aggregated fitness correspond to the min fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Genetic type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.01;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method genetic pop_dim: 3 crossover_prob: 0.7 mutation_prob: 0.1 improve_sol: true stochastic_sel: false
	nb_prelim_gen: 1 max_gen: 5  minimize: num_dead  aggregation: "min";
}


// This experiment explores two parameters with a Hill Climbing strategy from an explicit init solution (if no solution is given, start from a random sol),
// repeating each simulation three times (the aggregated fitness correspond to the max fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Hill_Climbing type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.1;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method hill_climbing init_solution:map(["infection_rate"::0.2, "dying_proba":: 0.05])  minimize: num_dead aggregation: "max";
}


// This experiment explores two parameters with a Tabu Search strategy,
// repeating each simulation three times (the aggregated fitness correspond to the max fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Tabu_Search type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.01;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method tabu iter_max: 5 tabu_list_size: 5 minimize: num_dead aggregation: "max";
}

// This experiment explores two parameters with a Reactive Tabu Search strategy,
// repeating each simulation three times (the aggregated fitness correspond to the max fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Reactive_Tabu_Search type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.01;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method reactive_tabu iter_max: 10 cycle_size_max: 10 cycle_size_min: 3 tabu_list_size_init: 5 minimize: num_dead aggregation: "max";
}

// This experiment explores two parameters with a Simulated annealing strategy,
// repeating each simulation three times (the aggregated fitness correspond to the max fitness), 
// in order to find the best combination of parameters to minimize the number of infected people
experiment Simulated_annealing type: batch keep_seed: true repeat: 3 until: ( time > 5000 ) {
	parameter 'Infection rate' var: infection_rate min: 0.1 max:0.5 step:0.01;
	parameter 'Probability of dying:' var: dying_proba min: 0.01 max: 0.2 step:0.01;
	method annealing  minimize: num_dead temp_decrease: 0.5 temp_end: 10.0 temp_init: 50;
}