Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/partnership share aligner #2

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified input/policy parameters.xlsx
Binary file not shown.
Binary file modified input/time_series_factor.xlsx
Binary file not shown.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<dependency>
<groupId>com.github.jasmineRepo</groupId>
<artifactId>JAS-mine-core</artifactId>
<version>4.3.2</version>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>com.github.jasmineRepo</groupId>
Expand Down
23 changes: 19 additions & 4 deletions src/main/java/simpaths/data/Parameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

// import plug-in packages
import simpaths.model.AnnuityRates;
import simpaths.model.SimPathsModel;
import simpaths.model.enums.*;
import org.apache.commons.collections4.keyvalue.MultiKey;
import org.apache.commons.collections4.map.LinkedMap;
Expand All @@ -21,7 +20,6 @@
import microsim.data.excel.ExcelAssistant;
import microsim.data.MultiKeyCoefficientMap;
import microsim.statistics.regression.*;
import microsim.engine.SimulationEngine;

// import LABOURsim packages
import simpaths.model.taxes.DonorTaxUnit;
Expand Down Expand Up @@ -349,8 +347,8 @@ else if(numberOfChildren <= 5) {

//Uprating factor
private static MultiKeyCoefficientMap upratingIndexMapGDP, upratingIndexMapInflation, socialCareProvisionTimeAdjustment,
upratingIndexMapWageGrowth, priceMapSavingReturns, priceMapDebtCostLow, priceMapDebtCostHigh,
wageRateFormalSocialCare, socialCarePolicy;
partnershipTimeAdjustment,upratingIndexMapWageGrowth, priceMapSavingReturns, priceMapDebtCostLow, priceMapDebtCostHigh,
wageRateFormalSocialCare, socialCarePolicy, partneredShare;
public static MultiKeyMap upratingFactorsMap = new MultiKeyMap<>();

//Education level projections
Expand Down Expand Up @@ -2399,15 +2397,19 @@ public static void loadTimeSeriesFactorMaps(Country country) {
upratingIndexMapInflation = ExcelAssistant.loadCoefficientMap("input/time_series_factor.xlsx", country.toString() + "_inflation", 1, 1);
upratingIndexMapWageGrowth = ExcelAssistant.loadCoefficientMap("input/time_series_factor.xlsx", country.toString() + "_wage_growth", 1, 1);
socialCareProvisionTimeAdjustment = ExcelAssistant.loadCoefficientMap("input/time_series_factor.xlsx", country.toString() + "_care_adjustment", 1, 1);
partnershipTimeAdjustment = ExcelAssistant.loadCoefficientMap("input/time_series_factor.xlsx", country.toString() + "_cohabitation_adjustment", 1, 1);

// rebase indices to base year defined by BASE_PRICE_YEAR
rebaseIndexMap(TimeSeriesVariable.GDP);
rebaseIndexMap(TimeSeriesVariable.Inflation);
rebaseIndexMap(TimeSeriesVariable.WageGrowth);
rebaseIndexMap(TimeSeriesVariable.CareProvisionAdjustment, startYear, false);
rebaseIndexMap(TimeSeriesVariable.PartnershipAdjustment, startYear, false);

// load year-specific fiscal policy parameters
socialCarePolicy = ExcelAssistant.loadCoefficientMap("input/policy parameters.xlsx", "social care", 1, 8);
partneredShare = ExcelAssistant.loadCoefficientMap("input/policy parameters.xlsx", "partnership", 1, 1);

}

public static void loadTimeSeriesFactorForTaxDonor(Country country) {
Expand Down Expand Up @@ -2466,6 +2468,10 @@ private static MultiKeyCoefficientMap getTimeSeriesValueMap(TimeSeriesVariable v
break;
case CareProvisionAdjustment:
map = socialCareProvisionTimeAdjustment;
break;
case PartnershipAdjustment:
map = partnershipTimeAdjustment;
break;
}

return map;
Expand Down Expand Up @@ -2663,6 +2669,15 @@ public static void setProjectWealth(boolean val) {
projectWealth = val;
}

public static double getPartnershipShare(int year) {

MultiKeyCoefficientMap map = partneredShare;
Object val = map.getValue(year);
if (val == null)
val = extendRateTimeSeries(year, map);
return ((Number) val).doubleValue();
}

public static double getSocialCarePolicyValue(int year, String param) {

Object val = socialCarePolicy.getRowColumnValue(year, param);
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/simpaths/data/RootSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ private double[] bisection() {
final int ITMAX = 200;

double[] xg, xs, xn=null, xp=null;
double fg, fs;
double fg, fs, fn, fp;

xg = target;
fg = function.evaluate(xg);
if (Math.abs(fg) > epsFunction) {
xg = target; // Start with initial values provided
fg = function.evaluate(xg); // Calculate the error using initial values and a provided alignment function, e.g. SocialCareAlignment or PartnershipAlignment
if (Math.abs(fg) > epsFunction) { // If error is larger than allowed threshold, conduct search for a better adjustment
// need to conduct search

targetAltered = true;
Expand All @@ -47,19 +47,23 @@ private double[] bisection() {
fs = function.evaluate(xs);
if (fs < 0.0) {
xn = xs;
fn = fs;
} else {
xp = xs;
fp = fs;
}
xs = upperBounds;
fs = function.evaluate(xs);
if (fs < 0.0) {
if (xn!=null)
throw new RuntimeException("Root search supplied boundaries that are both negative");
xn = xs;
fn = fs;
} else {
if (xp!=null)
throw new RuntimeException("Root search supplied boundaries that are both positive");
xp = xs;
fp = fs;
}

// start search
Expand All @@ -68,8 +72,10 @@ private double[] bisection() {

if (fg < 0.0) {
xn = xg;
fn = fg;
} else {
xp = xg;
fp = fg;
}
xg = midPoint(xn, xp);
fg = function.evaluate(xg);
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/simpaths/data/Statistics2.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ public class Statistics2 {
@Column(name= "social_care_adj_factor")
private double socialCareAdjustmentFactor;

@Column(name = "partnership_adj_factor")
private double partnershipAdjustmentFactor;

public double getPartnershipAdjustmentFactor() {
return partnershipAdjustmentFactor;
}

public void setPartnershipAdjustmentFactor(double partnershipAdjustmentFactor) {
this.partnershipAdjustmentFactor = partnershipAdjustmentFactor;
}
public double getSocialCareAdjustmentFactor() { return socialCareAdjustmentFactor; }
public void setSocialCareAdjustmentFactor(double factor) {socialCareAdjustmentFactor = factor;}

Expand Down Expand Up @@ -581,5 +591,6 @@ public void update(SimPathsModel model) {
setPopulation65to84(popula[2]);

setSocialCareAdjustmentFactor(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.CareProvisionAdjustment));
setPartnershipAdjustmentFactor(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.PartnershipAdjustment));
}
}
2 changes: 1 addition & 1 deletion src/main/java/simpaths/experiment/SimPathsMultiRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class SimPathsMultiRun extends MultiRun {

public static boolean executeWithGui = true;

private static int maxNumberOfRuns = 3;
private static int maxNumberOfRuns = 50;

private static String countryString;
private static int startYear;
Expand Down
109 changes: 109 additions & 0 deletions src/main/java/simpaths/model/PartnershipAlignment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package simpaths.model;

import microsim.engine.SimulationEngine;
import simpaths.data.IEvaluation;
import simpaths.data.Parameters;
import simpaths.model.enums.Dcpst;

import java.util.Set;

/**
* PartnershipAlignment adjusts the probability of individuals forming a union to values observed in the data.
* It modifies the intercept of the "considerCohabitation" probit model.
*
* To find the value by which the intercept should be adjusted, it uses a search routine.
* The routine adjusts probabilities, and performs union matching separate from the real matching in the model. If the
* results differ from the targets based on the data by more than a specified threshold, the adjustment is repeated.
*
* Importantly, the adjustment needs to be only found once. Modified intercepts can then be used in subsequent simulations.
*/

public class PartnershipAlignment implements IEvaluation {

private double aggregateShareOfPartneredPersons;
private double targetAggregateShareOfPartneredPersons;
private double partnershipAdjustment;
boolean partnershipAdjustmentChanged;
private Set<Person> persons;
private SimPathsModel model;

public PartnershipAlignment(Set<Person> persons, double partnershipAdjustment) {
this.model = (SimPathsModel) SimulationEngine.getInstance().getManager(SimPathsModel.class.getCanonicalName());
this.persons = persons;
this.partnershipAdjustment = partnershipAdjustment;
targetAggregateShareOfPartneredPersons = Parameters.getPartnershipShare(model.getYear());
}

/**
* Evaluates the discrepancy between the simulated and target aggregate share of partnered persons and adjusts partnerships if necessary.
*
* This method compares the adjustment parameter 'args[0]' with the current 'partnershipAdjustment'.
* If the absolute difference exceeds a small threshold (1.0E-5), it triggers the adjustment of partnerships.
*
* The error is then calculated as the difference between the target and the actual aggregate share of partnered persons.
* This error value is returned and serves as the stopping condition in root search routines.
*
* @param args An array of parameters, where args[0] represents the adjustment parameter.
* @return The error in the target aggregate share of partnered persons after potential adjustments.
*/
@Override
public double evaluate(double[] args) {

model.clearPersonsToMatch();
persons.stream()
.filter(person -> person.getDag() >= Parameters.MIN_AGE_COHABITATION)
.forEach(person -> person.evaluatePartnershipDissolution());

adjustPartnerships(args[0]);

double error = targetAggregateShareOfPartneredPersons - evalAggregateShareOfPartneredPersons();
return error;
}

/**
* Evaluates the aggregate share of persons with partners assigned in a test run of union matching among those eligible for partnership.
*
* This method uses Java streams to count the number of persons who meet the age criteria for cohabitation
* and the number of persons who currently have a test partner. The aggregate share is calculated as the
* ratio of successfully partnered persons to those eligible for partnership, with consideration for potential division by zero.
*
* @return The aggregate share of partnered persons among those eligible, or 0.0 if no eligible persons are found.
*/
private double evalAggregateShareOfPartneredPersons() {
long numPersonsWhoCanHavePartner = persons.stream()
.filter(person -> person.getDag() >= Parameters.MIN_AGE_COHABITATION)
.count();

long numPersonsPartnered = persons.stream()
.filter(person -> (person.hasTestPartner() || (person.getDcpst().equals(Dcpst.Partnered)) && !person.hasLeftPartnerTest()))
.count();

return numPersonsWhoCanHavePartner > 0
? (double) numPersonsPartnered / numPersonsWhoCanHavePartner
: 0.0;
}

/**
* Adjusts the probit regression used for partnership evaluation and re-evaluates the score for all eligible persons.
* Then, creates "test" unions between individuals.
*
* This method performs the following steps:
* 1. Runs the cohabitation probit model.
* 2. Matches individuals within this method.
* TODO: the loops over persons calculating cohabitation probability should be paralellised
* @param newPartnershipAdjustment The new adjustment value for the partnership probit regression.
*/
private void adjustPartnerships(double newPartnershipAdjustment) {
persons.stream()
.filter(person -> person.getDag() >= Parameters.MIN_AGE_COHABITATION)
.forEach(person -> person.evaluatePartnershipFormation(newPartnershipAdjustment));

// "Fake" union matching (not modifying household structure) here
model.unionMatching(true);
model.unionMatchingNoRegion(true);

partnershipAdjustment = newPartnershipAdjustment;
partnershipAdjustmentChanged = true;
}

}
Loading