Commit d1f67ce0 authored by Dumoulin Nicolas's avatar Dumoulin Nicolas
Browse files

NEW Population dynamics implementation with initialization in Generator

parent ae32b4f5
package fr.inrae.agriterix.generator;
import com.thoughtworks.xstream.XStream;
import fr.inrae.agriterix.simulator.CulturalPractice;
import fr.inrae.agriterix.simulator.Farm;
import fr.inrae.agriterix.simulator.Farmer;
......@@ -16,18 +17,19 @@ import fr.inrae.agriterix.simulator.Unit;
import fr.inrae.agriterix.simulator.dynamics.Dynamics;
import fr.inrae.agriterix.simulator.dynamics.FlowDynamics;
import fr.inrae.agriterix.simulator.dynamics.MarketDynamics;
import fr.inrae.agriterix.simulator.dynamics.PopulationDynamics;
import fr.inrae.agriterix.simulator.dynamics.PopulationDynamicsByAgeClass;
import fr.inrae.agriterix.simulator.dynamics.ProductionDynamics;
import fr.inrae.agriterix.simulator.dynamics.transmission.TransmissionTendancy;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
......@@ -38,50 +40,54 @@ import umontreal.ssj.rng.MRG32k3a;
import umontreal.ssj.rng.RandomStreamBase;
public class Generator {
/**
* Build a list of farmers with given initial distribution of ages (starting from 18 yo).
* Build a list of farmers with given initial distribution of ages (starting
* from 18 yo).
*
* @ages array of effectives for each ages
* @area the GeoArea instance for initializing the farmers
* @popTotal the total number of farmers to build.
*/
private static List<Farmer> generate_farmers(int[] ages, GeoArea area, int popTotal) {
int sum = Arrays.stream(ages).sum();
double[] probas = Arrays.stream(ages).asDoubleStream().map(i -> i/sum).toArray();
int[] population = Arrays.stream(probas).mapToInt(i -> (int)Math.round(i * popTotal)).toArray();
double[] probas = Arrays.stream(ages).asDoubleStream().map(i -> i / sum).toArray();
int[] population = Arrays.stream(probas).mapToInt(i -> (int) Math.round(i * popTotal)).toArray();
List<Farmer> farmers = new ArrayList<>();
for (int i = 0; i < population.length; i++) {
for (int j = 0; j < population[i]; j++) {
Farm farm = new Farm(area);
farmers.add(new Farmer(i+18, farm, i));
farmers.add(new Farmer(i + 18, farm, i));
}
}
return farmers;
}
/**
* Randomly pick and remove a farmer in the given list
*
* @param rng the RNG to use
* @param farmers the initial list of farmers
* @param id_expl the ID to set to the picked farmer.
* @return The farmer
*/
private static Farmer pickFarmer(RandomStreamBase rng, List<Farmer> farmers, int id_expl) {
Farmer farmer = farmers.remove(rng.nextInt(0, farmers.size()-1));
Farmer farmer = farmers.remove(rng.nextInt(0, farmers.size() - 1));
farmer.setId(id_expl);
return farmer;
}
/**
* Generates patches in the given geo area.
*
* @param rng The RNG used for picking the farmer
* @param shapefile The shapefile containing the patches
* @param area The initial area where put the patches
* @param farmers The list of farmers to assign on the patches (unmodified)
* @return the list of products produced by the patches
* @throws IOException
* @throws IOException
*/
public static List<Product> generate_patches(RandomStreamBase rng, String shapefile, GeoArea area, List<Farmer> farmers) throws IOException {
public static List<Product> generate_patches(RandomStreamBase rng, String shapefile, GeoArea area, List<Farmer> farmers) throws IOException {
Map<String, Product> products = new HashMap<>();
Map<Integer, Farmer> farmersID = new HashMap<>();
List<Patch> patches = new ArrayList<>();
......@@ -128,9 +134,10 @@ public class Generator {
/**
* Creates a dummy flow for a given product
*
* @param product
* @param maximum
* @return
* @return
*/
private static Flow createFlow(Product product, float maximum) {
Stocks quantities1 = new Stocks();
......@@ -146,6 +153,25 @@ public class Generator {
return new Flow(inputs1, quantities1, outputs1, ratio1, "Commerce");
}
public static Map<String, Map<String, Double>> createTransMatrix(List<Product> products) throws IOException {
// for test minimal
XStream xStream = new XStream();
XStream.setupDefaultSecurity(xStream);
xStream.allowTypesByRegExp(new String[]{".*"});
Map<String, Map<String, Double>> transitions = new ConcurrentHashMap<>();
for (int i = 0; i < products.size(); i++) {
Map<String, Double> transitionsP = new ConcurrentHashMap<>();
for (int j = 0; j < products.size(); j++) {
transitionsP.put(products.get(j).getLabel(), i == j ? 0.9 : 0.1 / (products.size() - 1));
}
transitions.put(products.get(i).getLabel(), transitionsP);
}
return transitions;
}
public static void main(String[] args) throws IOException {
// farmers ages from MSA for France, starting at 18 year old
int[] ages_2018 = {1, 76, 268, 579, 1079, 1493, 1973, 2432, 3059, 3592, 4236, 4717, 5440, 5904, 6419, 6945, 7574, 7847, 8414, 8704, 8992, 8782, 8821, 9069, 9236, 9741, 10569, 11458, 12058, 12623, 12927, 13351, 13637, 14446, 15461, 16145, 17113, 17017, 17163, 17660, 17350, 17190, 16147, 12294, 10037, 6746, 5509, 4670, 3720, 2830, 2401, 2181, 1856, 1584, 1209, 790, 665, 595, 496, 390, 333, 346, 303, 246, 238, 205, 201, 175, 146, 125, 92, 82, 75, 55, 50, 36, 36, 26, 25, 18, 13, 10, 11};
......@@ -157,7 +183,10 @@ public class Generator {
geoarea.getFlows().add(createFlow(product, 100.f));
}
List<Dynamics> dynamics = new ArrayList<>();
//dynamics.add(new PopulationDynamics());
dynamics.add(new PopulationDynamicsByAgeClass(
5.31966309e-04,3.02391767e+01, 3.89377418e-01, 7.46514412e+00, 4.27098464e+01, 1.58988450e-01,
new TransmissionTendancy(0.5, 0.5, 3, createTransMatrix(products))
));
dynamics.add(new ProductionDynamics());
dynamics.add(new MarketDynamics());
dynamics.add(new FlowDynamics());
......
......@@ -76,6 +76,10 @@ public class Patch {
return production;
}
public void setProduction(Production production) {
this.production = production;
}
/**
* Set the farm where this patch and add the patch into the farm.
*
......
......@@ -115,4 +115,38 @@ public class SimulationContext {
return NormalGen.nextDouble(randomStream, m, std);
}
/**
* Picks out randomly an object in the given list.
*
* @param <T>
* @param objects
* @return
*/
public <T> T nextObject(List<T> objects) {
return nextObject(objects, false);
}
/**
* Picks out randomly an object in the given list.
*
* @param <T>
* @param objects
* @param remove if true, the returned object is removed from the list
* @return null if the list is null or empty.
*/
public <T> T nextObject(List<T> objects, boolean remove) {
if (objects == null || objects.isEmpty()) {
return null;
}
if (remove) {
return objects.remove(nextInt(0, objects.size() - 1));
} else {
return objects.get(nextInt(0, objects.size() - 1));
}
}
public boolean nextBoolean() {
return nextDouble() < 0.5;
}
}
package fr.inrae.agriterix.simulator.dynamics;
import fr.inrae.agriterix.simulator.*;
import fr.inrae.agriterix.simulator.dynamics.transmission.TransmissionStrategy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
......@@ -8,11 +10,24 @@ import java.util.stream.IntStream;
import umontreal.ssj.probdist.BetaDist;
@Deprecated
public class PopulationDynamicsByAgeClass extends Dynamics {
private double amplitude, drop_x, mu, v, r_threshold, r_factor;
private TransmissionStrategy transmission;
public PopulationDynamicsByAgeClass() {
}
public PopulationDynamicsByAgeClass(double amplitude, double drop_x, double mu, double v, double r_threshold, double r_factor, TransmissionStrategy transmission) {
this.amplitude = amplitude;
this.drop_x = drop_x;
this.mu = mu;
this.v = v;
this.r_threshold = r_threshold;
this.r_factor = r_factor;
this.transmission = transmission;
}
@Override
public void process(SimulationContext context, List<GeoArea> geoAreas, List<Farmer> farmers, Flow global,
Parameters parameters, TimeUnit timeUnit) {
......@@ -24,18 +39,27 @@ public class PopulationDynamicsByAgeClass extends Dynamics {
int popSum = IntStream.of(ageclasses).sum();
double alpha = mu * v;
double beta = (1 - mu) * v;
int i = 0;
int[] installations = IntStream.range(0, ageclasses.length).map(
idx -> (int) Math.round(ageclasses[idx] * popSum * amplitude
* BetaDist.density(alpha, beta, idx / drop_x))
).toArray();
List<Integer> installationsAges = new ArrayList<>();
for (int i = 0; i < installations.length; i++) {
for (int j = 0; j < installations[i]; j++) {
installationsAges.add(getAge(i));
}
}
int[] retirements = IntStream.range(0, ageclasses.length).map(
idx -> idx>=r_threshold ? (int)Math.round(r_factor * ageclasses[idx]) : 0
).toArray();
// TODO apply installations and retirements
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
transmission.process(context, geoAreas, farmers, installationsAges, retirements);
}
private int getAge(int ageclass) {
return ageclass + 18;
}
@Override
public void initLogger() {
logger = new DynamicsLogger(this.getClass().getSimpleName(), super.timeStamp);
......
package fr.inrae.agriterix.simulator.dynamics.transmission;
import fr.inrae.agriterix.simulator.Farmer;
import fr.inrae.agriterix.simulator.GeoArea;
import fr.inrae.agriterix.simulator.SimulationContext;
import java.util.List;
public interface TransmissionStrategy {
public void process(SimulationContext context, List<GeoArea> geoAreas, List<Farmer> farmers, List<Integer> installationsAges, int[] retirements);
}
package fr.inrae.agriterix.simulator.dynamics.transmission;
import fr.inrae.agriterix.simulator.Farmer;
import fr.inrae.agriterix.simulator.GeoArea;
import fr.inrae.agriterix.simulator.Patch;
import fr.inrae.agriterix.simulator.Product;
import fr.inrae.agriterix.simulator.SimulationContext;
import fr.inrae.agriterix.simulator.Unit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TransmissionTendancy implements TransmissionStrategy {
// Is the transmission realized with only one successor?
// TODO better with a distribution on number of successors
private double singleSuccessorProba;
// Is the transmission realized with an existant farmer (instead of a new)
private double existantSuccessorProba;
private int maxSuccessors;
private Map<String, Map<String, Double>> transitionMatrix;
public TransmissionTendancy() {
}
public TransmissionTendancy(double singleSuccessorProba, double existantSuccessorProba, int maxSuccessors, Map<String, Map<String, Double>> transitionMatrix) {
this.singleSuccessorProba = singleSuccessorProba;
this.existantSuccessorProba = existantSuccessorProba;
this.maxSuccessors = maxSuccessors;
this.transitionMatrix = transitionMatrix;
}
@Override
public void process(SimulationContext context, List<GeoArea> geoAreas, List<Farmer> farmers, List<Integer> installationsAges, int[] retirements) {
// retrieving farmers that will retire
List<Farmer> retiredFarmers = new ArrayList<>(); // TODO useless?
for (int i = 0; i < retirements.length; i++) {
int nbRetired = retirements[i];
final int age = i;
List<Farmer> ageIFarmers = farmers.stream().filter(f -> f.getAge() == age).collect(Collectors.toList());
for (int j = 0; j < nbRetired; j++) {
final Farmer retiredFarmer = context.nextObject(ageIFarmers, true);
farmers.remove(retiredFarmer);
retiredFarmers.add(retiredFarmer);
if (context.nextDouble() <= singleSuccessorProba) {
processSuccessor(installationsAges, context, farmers, retiredFarmer);
} else { // multiple successors
List<Farmer> successors = new ArrayList<>();
for (int k = 0; k < context.nextInt(2, maxSuccessors); k++) {
//successors.add(context.nextObject(farmers));
Farmer existingFarmer = context.nextObject(successors, false);
existingFarmer = context.nextObject(successors, false);
processSuccessor(installationsAges, context, farmers, retiredFarmer); // new farmer
}
}
}
}
}
private void processSuccessor(List<Integer> installationsAges, SimulationContext context, List<Farmer> farmers, final Farmer retiredFarmer) {
// single successor
if (installationsAges.isEmpty() || context.nextDouble() <= existantSuccessorProba) {
// existant farmer
Farmer successor = context.nextObject(farmers, false);
patchTransition(retiredFarmer, successor, context);
} else {
// new farmer
Farmer newFarmer = new Farmer(context.nextObject(installationsAges, true), retiredFarmer.getFarm(), context.setIdFarmer());
patchTransition(retiredFarmer, newFarmer, context);
}
}
private void patchTransition(final Farmer retiredFarmer, Farmer successor, SimulationContext context) {
for (Patch patch : retiredFarmer.getFarm().getPatches()) {
if (retiredFarmer.getFarm() != successor.getFarm()) {
patch.setFarm(successor.getFarm());
}
String product = context.nextMapObjectWithDistributionInKeysBis(
transitionMatrix.get(patch.getProduction().getProduct().getLabel())
);
if (!product.equals(patch.getProduction().getProduct().getLabel())) {
// TODO correctly fetch existing product or good initialization
patch.setProduction(new Patch.Production(new Product(product, Unit.Kg), 0.f));
}
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment