Skip to content

Commit b3a39c6

Browse files
Merge pull request #221 from centreformicrosimulation/experimental/lifetime_draws
Experimental/lifetime draws
2 parents 3c2398f + ddd2d82 commit b3a39c6

8 files changed

Lines changed: 230 additions & 51 deletions

File tree

input/reg_labourSupplyUtility.xlsx

-47.8 KB
Binary file not shown.

src/main/java/simpaths/data/Parameters.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
import org.apache.commons.math3.distribution.MultivariateNormalDistribution;
1919
import org.apache.commons.math3.distribution.NormalDistribution;
2020
import org.apache.commons.math3.util.Pair;
21-
import simpaths.data.startingpop.DataParser;
22-
import simpaths.model.AnnuityRates;
2321
import simpaths.model.decisions.Grids;
24-
import simpaths.model.enums.*;
2522
import simpaths.model.taxes.DonorTaxUnit;
2623
import simpaths.model.taxes.MatchFeature;
2724
import simpaths.model.taxes.database.TaxDonorDataParser;
@@ -241,6 +238,10 @@ else if(numberOfChildren <= 5) {
241238
public static final double WEEKS_PER_MONTH = 365.25/(7.*12.); // = 4.348214286
242239
public static final double WEEKS_PER_YEAR = 365.25 / 7.;
243240

241+
// Determine probability of yearly labour supply matches persisting from previous year
242+
public static double labour_innovation_employment_persistence_probability = 0.9;
243+
public static double labour_innovation_notinemployment_persistence_probability = 0.1;
244+
244245
public static final int HOURS_IN_WEEK = 24 * 7; //This is used to calculate leisure in labour supply
245246
//Is it possible for people to start going to the labour module (e.g. age 17) while they are living with parents (until age 18)?
246247
//Cannot see how its possible if it is the household that decides how much labour to supply. If someone finishes school at 17, they need to leave home before they can enter the labour market. So set age for finishing school and leaving home to 18.
@@ -277,8 +278,8 @@ else if(numberOfChildren <= 5) {
277278

278279
//public static int MAX_AGE_IN_EDUCATION;// = MAX_AGE;//30; // Max age a person can stay in education //Cannot set here, as MAX_AGE is not known yet. Now set to MAX_AGE in buildObjects in Model class.
279280
//public static int MAX_AGE_MARRIAGE;// = MAX_AGE;//75; // Max age a person can marry //Cannot set here, as MAX_AGE is not known yet. Now set to MAX_AGE in buildObjects in Model class.
280-
private static int MIN_START_YEAR = 2011; //Minimum allowed starting point. Should correspond to the oldest initial population.
281-
private static int MAX_START_YEAR = 2021; //Maximum allowed starting point. Should correspond to the most recent initial population.
281+
private static int MIN_START_YEAR = 2015; //Minimum allowed starting point. Should correspond to the oldest initial population.
282+
private static int MAX_START_YEAR = 2019; //Maximum allowed starting point. Should correspond to the most recent initial population.
282283
public static int startYear;
283284
public static int endYear;
284285
private static final int MIN_START_YEAR_TESTING = 2019;
@@ -1139,10 +1140,10 @@ else if(country.equals(Country.UK)) {
11391140
columnsEmploymentSelectionFemalesE = 29;
11401141
columnsLabourSupplyUtilityMales = 19;
11411142
columnsLabourSupplyUtilityFemales = 12;
1142-
columnsLabourSupplyUtilityMalesWithDependent = 23;
1143-
columnsLabourSupplyUtilityFemalesWithDependent = 23;
1144-
columnsLabourSupplyUtilityACMales = 17;
1145-
columnsLabourSupplyUtilityACFemales = 17;
1143+
columnsLabourSupplyUtilityMalesWithDependent = 15;
1144+
columnsLabourSupplyUtilityFemalesWithDependent = 15;
1145+
columnsLabourSupplyUtilityACMales = 31;
1146+
columnsLabourSupplyUtilityACFemales = 31;
11461147
columnsLabourSupplyUtilityCouples = 64;
11471148
columnsLabourCovid19_SE = 1;
11481149
columnsLabourCovid19_2a_processes = 1;

src/main/java/simpaths/model/BenefitUnit.java

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public class BenefitUnit implements EventListener, IDoubleSource, Weight, Compar
9595
@Transient ArrayList<Triple<Les_c7_covid, Double, Integer>> covid19MonthlyStateAndGrossIncomeAndWorkHoursTripleFemale = new ArrayList<>(); // This ArrayList stores monthly values of labour market states and gross incomes, to be sampled from by the LabourMarket class, for the female member of the benefit unit
9696

9797
@Transient Innovations innovations;
98+
@Transient private Double lastLabourInnov;
99+
@Transient private Integer lastYear;
98100

99101
@Transient private Integer yearLocal;
100102
@Transient private Occupancy occupancyLocal;
@@ -153,7 +155,7 @@ public BenefitUnit(Long id, long seed) {
153155
key = new PanelEntityKey(id); //Sets up key
154156

155157
this.seed = seed;
156-
innovations = new Innovations(9, seed);
158+
innovations = new Innovations(9, 1, seed);
157159

158160
this.numberChildrenAll_lag1 = 0;
159161
this.numberChildren02_lag1 = 0;
@@ -1108,7 +1110,28 @@ protected void updateLabourSupplyAndIncome() {
11081110
}
11091111

11101112
//Sample labour supply from possible labour (pairs of) values
1111-
double labourInnov = innovations.getDoubleDraw(5);
1113+
double labourInnov = 0;
1114+
1115+
// Check if:
1116+
// - single occupant is employed
1117+
// - or male of couple is employed
1118+
// - or male is not at risk of work and female is employed
1119+
// -> then persist employment at prob_{persist_employment}
1120+
// Otherwise
1121+
// -> persist unemployment at prob_{persist_unemployment}
1122+
1123+
if (occupancy.Single_Male.equals(occupancy) && male.atRiskOfWork() && male.getEmployed() == 1) {
1124+
labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability);
1125+
} else if (occupancy.Single_Female.equals(occupancy) && female.atRiskOfWork() && female.getEmployed() == 1) {
1126+
labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability);
1127+
} else if (occupancy.equals(Occupancy.Couple) &&
1128+
((male.atRiskOfWork() && male.getEmployed() == 1) ||
1129+
(!male.atRiskOfWork() && female.atRiskOfWork() && female.getEmployed() == 1))) {
1130+
labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability);
1131+
} else {
1132+
labourInnov = getLabourInnovation(Parameters.labour_innovation_notinemployment_persistence_probability);
1133+
}
1134+
11121135
try {
11131136
MultiKeyMap<Labour, Double> labourSupplyUtilityRegressionProbabilitiesByLabourPairs = convertRegressionScoresToProbabilities(labourSupplyUtilityRegressionScoresByLabourPairs);
11141137
labourSupplyChoice = ManagerRegressions.multiEvent(labourSupplyUtilityRegressionProbabilitiesByLabourPairs, labourInnov);
@@ -1529,6 +1552,30 @@ public enum Regressors {
15291552
MaleEduH_30,
15301553
MaleEduM_40,
15311554
MaleEduH_40,
1555+
MaleEduM_1,
1556+
MaleEduH_1,
1557+
MaleEduM_2,
1558+
MaleEduH_2,
1559+
MaleEduM_3,
1560+
MaleEduH_3,
1561+
MaleEduM_4,
1562+
MaleEduH_4,
1563+
FemaleEduM_10,
1564+
FemaleEduH_10,
1565+
FemaleEduM_20,
1566+
FemaleEduH_20,
1567+
FemaleEduM_30,
1568+
FemaleEduH_30,
1569+
FemaleEduM_40,
1570+
FemaleEduH_40,
1571+
FemaleEduM_1,
1572+
FemaleEduH_1,
1573+
FemaleEduM_2,
1574+
FemaleEduH_2,
1575+
FemaleEduM_3,
1576+
FemaleEduH_3,
1577+
FemaleEduM_4,
1578+
FemaleEduH_4,
15321579
MaleLeisure_dnc,
15331580
FemaleLeisure_dnc,
15341581
MaleLeisure_dnc02,
@@ -2416,6 +2463,78 @@ public double getDoubleValue(Enum<?> variableID) {
24162463
case MaleEduH_40 -> {
24172464
return (getMale() != null && getMale().getLabourSupplyWeekly().equals(Labour.FORTY) && getMale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
24182465
}
2466+
case MaleEduM_1 -> {
2467+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && getMale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2468+
}
2469+
case MaleEduH_1 -> {
2470+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && getMale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2471+
}
2472+
case MaleEduM_2 -> {
2473+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getMale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2474+
}
2475+
case MaleEduH_2 -> {
2476+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getMale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2477+
}
2478+
case MaleEduM_3 -> {
2479+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getMale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2480+
}
2481+
case MaleEduH_3 -> {
2482+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getMale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2483+
}
2484+
case MaleEduM_4 -> {
2485+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.FORTY) && getMale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2486+
}
2487+
case MaleEduH_4 -> {
2488+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.FORTY) && getMale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2489+
}
2490+
case FemaleEduM_10 -> {
2491+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2492+
}
2493+
case FemaleEduH_10 -> {
2494+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2495+
}
2496+
case FemaleEduM_20 -> {
2497+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2498+
}
2499+
case FemaleEduH_20 -> {
2500+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2501+
}
2502+
case FemaleEduM_30 -> {
2503+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2504+
}
2505+
case FemaleEduH_30 -> {
2506+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2507+
}
2508+
case FemaleEduM_40 -> {
2509+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.FORTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2510+
}
2511+
case FemaleEduH_40 -> {
2512+
return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.FORTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2513+
}
2514+
case FemaleEduM_1 -> {
2515+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2516+
}
2517+
case FemaleEduH_1 -> {
2518+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2519+
}
2520+
case FemaleEduM_2 -> {
2521+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2522+
}
2523+
case FemaleEduH_2 -> {
2524+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2525+
}
2526+
case FemaleEduM_3 -> {
2527+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2528+
}
2529+
case FemaleEduH_3 -> {
2530+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2531+
}
2532+
case FemaleEduM_4 -> {
2533+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.FORTY) && getFemale().getDeh_c3().equals(Education.Medium)) ? 1. : 0.;
2534+
}
2535+
case FemaleEduH_4 -> {
2536+
return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.FORTY) && getFemale().getDeh_c3().equals(Education.High)) ? 1. : 0.;
2537+
}
24192538
case MaleLeisure_dnc02 -> {
24202539
return (Parameters.HOURS_IN_WEEK - getMale().getLabourSupplyHoursWeekly()) * getIndicatorChildren(0,1).ordinal();
24212540
}
@@ -3646,4 +3765,55 @@ public void removeMember(Person member) {
36463765
}
36473766

36483767
public static void setBenefitUnitIdCounter(long id) {benefitUnitIdCounter = id;}
3768+
3769+
/**
3770+
* Calculates the labour innovation value for the current period with persistence.
3771+
*
3772+
* This method implements a persistence mechanism for labour innovations where there's
3773+
* a probability that the previous period's innovation will persist into the current period.
3774+
* A new innovation is drawn each period, but it's only used with probability (1 - persistenceProbability).
3775+
*
3776+
* The persistence mechanism works as follows:
3777+
* 1. Draw a new potential innovation value
3778+
* 2. Draw a random number to determine if we should use the new value or persist with the old one
3779+
* 3. If it's the first period or there was a gap in years, use the new innovation
3780+
* 4. Otherwise, use the persistence probability to decide between new and old values
3781+
*
3782+
* @param persistenceProbability The probability (between 0 and 1) that the previous period's
3783+
* innovation will persist. A value of 0 means always use new innovations,
3784+
* while 1 means always keep the old innovation.
3785+
* @return The labour innovation value for this period
3786+
*/
3787+
private double getLabourInnovation(double persistenceProbability) {
3788+
3789+
double newLabourInnovation = innovations.getDoubleDraw(5);
3790+
3791+
// persistenceRandomDraw - random value to determine if we keep old innovation
3792+
double persistenceRandomDraw = innovations.getDoubleDraw(6);
3793+
3794+
int currentYear = model.getYear();
3795+
3796+
// Initialize on first call
3797+
if (lastLabourInnov == null || lastYear == null) {
3798+
lastLabourInnov = newLabourInnovation;
3799+
lastYear = currentYear;
3800+
return newLabourInnovation;
3801+
}
3802+
3803+
double labourInnov;
3804+
if (persistenceRandomDraw < persistenceProbability) {
3805+
labourInnov = lastLabourInnov;
3806+
} else {
3807+
labourInnov = newLabourInnovation;
3808+
}
3809+
3810+
// Store values for next period
3811+
lastLabourInnov = labourInnov;
3812+
lastYear = currentYear;
3813+
3814+
return labourInnov;
3815+
3816+
3817+
3818+
}
36493819
}

src/main/java/simpaths/model/Innovations.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ public Innovations(int nDoubleInnovs, int nSingleDrawDoubleInnovs, int nSingleDr
2828
}
2929
}
3030

31+
public Innovations(int nDoubleInnovs, int nSingleDrawDoubleInnovs, long seed) {
32+
this(nDoubleInnovs, seed);
33+
singleDrawDoubleInnovs = new double[nSingleDrawDoubleInnovs];
34+
for (int ii = 0; ii < nSingleDrawDoubleInnovs; ii++) {
35+
singleDrawDoubleInnovs[ii] = generator.nextDouble();
36+
}
37+
}
38+
3139
public void getNewDoubleDraws() {
3240
for (int ii = 0; ii < doubleInnovs.length; ii++) {
3341
doubleInnovs[ii] = generator.nextDouble();

0 commit comments

Comments
 (0)