|
| 1 | +package net.finmath.experiments.ui; |
| 2 | + |
| 3 | +import java.text.DecimalFormat; |
| 4 | +import java.util.Arrays; |
| 5 | +import java.util.List; |
| 6 | +import java.util.Map; |
| 7 | +import java.util.function.BooleanSupplier; |
| 8 | +import java.util.function.DoubleConsumer; |
| 9 | +import java.util.function.UnaryOperator; |
| 10 | +import java.util.stream.Collectors; |
| 11 | + |
| 12 | +import net.finmath.climate.models.ClimateModel; |
| 13 | +import net.finmath.climate.models.dice.DICEModel; |
| 14 | +import net.finmath.climateschool.utilities.AdamOptimizerUsingFiniteDifferences; |
| 15 | +import net.finmath.climateschool.utilities.AdamOptimizerUsingFiniteDifferences.GradientMethod; |
| 16 | +import net.finmath.climateschool.utilities.DICEModelPlots; |
| 17 | +import net.finmath.experiments.ui.parameter.BooleanParameter; |
| 18 | +import net.finmath.experiments.ui.parameter.DoubleParameter; |
| 19 | +import net.finmath.stochastic.RandomVariable; |
| 20 | +import net.finmath.stochastic.Scalar; |
| 21 | +import net.finmath.time.TimeDiscretization; |
| 22 | +import net.finmath.time.TimeDiscretizationFromArray; |
| 23 | + |
| 24 | +public class DICECalibrationExperimentUI extends ExperimentUI { |
| 25 | + |
| 26 | + private static final double timeStep = 1.0; |
| 27 | + private static final double timeHorizon = 500.0; |
| 28 | + |
| 29 | + private final DecimalFormat numberDigit3 = new DecimalFormat("#.000"); |
| 30 | + private final DecimalFormat numberPercent2 = new DecimalFormat("#.00%"); |
| 31 | + |
| 32 | + double[] initialParameters; |
| 33 | + |
| 34 | + private final DICEModelPlots plots = new DICEModelPlots(); |
| 35 | + |
| 36 | + public DICECalibrationExperimentUI() { |
| 37 | + super(List.of( |
| 38 | + new DoubleParameter("Discount Rate", 0.03, 0.01, 0.05), |
| 39 | + new BooleanParameter("Show Cost", false) |
| 40 | + // new Parameter("Abatement Max Time", 50.0, 10.0, 200.0) |
| 41 | + )); |
| 42 | + } |
| 43 | + |
| 44 | + public String getTitle() { return "DICE Model - Full Abatement Model - Optimized Emisison Path (Calibration)"; } |
| 45 | + |
| 46 | + public void runCalculation(BooleanSupplier isCancelled, DoubleConsumer progress) { |
| 47 | + Map<String, Object> currentParameterSet = getExperimentParameters().stream().collect(Collectors.toMap(p -> p.getBindableValue().getName(), p -> p.getBindableValue().getValue())); |
| 48 | + |
| 49 | + System.out.println("Calculation with Parameters: " + currentParameterSet); |
| 50 | + |
| 51 | + /* |
| 52 | + * Create a time discretization |
| 53 | + */ |
| 54 | + final int numberOfTimeSteps = (int)Math.round(timeHorizon / timeStep); |
| 55 | + final TimeDiscretization timeDiscretization = new TimeDiscretizationFromArray(0.0, numberOfTimeSteps, timeStep); |
| 56 | + |
| 57 | + /* |
| 58 | + * Create our savings rate model: a constant. |
| 59 | + */ |
| 60 | + final UnaryOperator<Double> savingsRateFunction = time -> 0.26; |
| 61 | + |
| 62 | + /* |
| 63 | + * Discount rate |
| 64 | + */ |
| 65 | + final double discountRate = (Double)currentParameterSet.get("Discount Rate"); |
| 66 | + |
| 67 | + // Initial parameters for our abatement function |
| 68 | + if(initialParameters == null) { |
| 69 | + initialParameters = new double[timeDiscretization.getNumberOfTimes()]; |
| 70 | + Arrays.fill(initialParameters, -Math.log(-Math.log(0.8))); |
| 71 | + } |
| 72 | + |
| 73 | + final AdamOptimizerUsingFiniteDifferences optimizer = new AdamOptimizerUsingFiniteDifferences(initialParameters, 800, 0.05, GradientMethod.AVERAGE) { |
| 74 | + private int iteration = 0; |
| 75 | + @Override |
| 76 | + public RandomVariable setValue(RandomVariable[] parameters) { |
| 77 | + if(Thread.currentThread().isInterrupted()) { |
| 78 | + this.stop(); |
| 79 | + } |
| 80 | + |
| 81 | + double[] abatementParameter = Arrays.stream(parameters).mapToDouble(RandomVariable::getAverage).map(x -> Math.exp(-Math.exp(-x))).toArray(); |
| 82 | + abatementParameter[0] = 0.03; |
| 83 | + initialParameters = Arrays.stream(parameters).mapToDouble(RandomVariable::getAverage).toArray(); |
| 84 | + |
| 85 | + /* |
| 86 | + * Create our abatement model |
| 87 | + */ |
| 88 | + final UnaryOperator<Double> abatementFunction = time -> abatementParameter[(int)Math.round(time/timeStep)]; |
| 89 | + |
| 90 | + /* |
| 91 | + * Create the DICE model |
| 92 | + */ |
| 93 | + final ClimateModel climateModel = new DICEModel(timeDiscretization, abatementFunction, savingsRateFunction, discountRate); |
| 94 | + |
| 95 | + final double value = climateModel.getValue().expectation().doubleValue(); |
| 96 | + |
| 97 | + // Penalty for non-smoothness - it works without this, but this helps the optimizer to avoid onszillations (that are la |
| 98 | + double roughness = 0.0; |
| 99 | + for(int i=1; i<abatementParameter.length-2; i++) { |
| 100 | + roughness += Math.pow(abatementParameter[i+1] - abatementParameter[i], 2.0); |
| 101 | + } |
| 102 | + roughness = Math.sqrt(roughness); |
| 103 | + roughness /= abatementParameter.length; |
| 104 | + roughness *= 0.1; |
| 105 | + |
| 106 | + // Update the plot every 200 iterations |
| 107 | + if(iteration%200 == 0 && !Thread.currentThread().isInterrupted()) { |
| 108 | + String spec = "r = " + numberPercent2.format(discountRate) + "; value = " + numberDigit3.format(value); |
| 109 | + plots.plot(climateModel, spec); |
| 110 | + boolean showCost = (boolean)currentParameterSet.get("Show Cost"); |
| 111 | + if(showCost) { |
| 112 | + plots.plotCost(climateModel, discountRate, spec); |
| 113 | + } |
| 114 | + else { |
| 115 | + plots.closeCost(); |
| 116 | + } |
| 117 | + } |
| 118 | + iteration++; |
| 119 | + |
| 120 | + return Scalar.of(-value);// + roughness); |
| 121 | + } |
| 122 | + }; |
| 123 | + |
| 124 | + optimizer.run(); |
| 125 | + |
| 126 | + System.out.println("Optimizer finished."); |
| 127 | + |
| 128 | + // Get optimal value |
| 129 | + final RandomVariable[] bestParameters = optimizer.getBestFitParameters(); |
| 130 | + double[] abatementParameter = Arrays.stream(bestParameters).mapToDouble(RandomVariable::getAverage).map(x -> Math.exp(-Math.exp(-x))).toArray(); |
| 131 | + abatementParameter[0] = 0.03; |
| 132 | + System.out.println(Arrays.toString(abatementParameter)); |
| 133 | + |
| 134 | + /* |
| 135 | + * Create our abatement model |
| 136 | + */ |
| 137 | + final UnaryOperator<Double> abatementFunction = time -> abatementParameter[(int)Math.round(time/timeStep)]; |
| 138 | + |
| 139 | + /* |
| 140 | + * Create the DICE model |
| 141 | + */ |
| 142 | + final ClimateModel climateModel = new DICEModel(timeDiscretization, abatementFunction, savingsRateFunction, discountRate); |
| 143 | + |
| 144 | + /* |
| 145 | + * Plots |
| 146 | + */ |
| 147 | + |
| 148 | + if(!Thread.currentThread().isInterrupted() && !isCancelled.getAsBoolean()) { |
| 149 | + synchronized(this) { |
| 150 | + String spec = "r = " + numberPercent2.format(discountRate); |
| 151 | + plots.plot(climateModel, spec); |
| 152 | + |
| 153 | + boolean showCost = (boolean)currentParameterSet.get("Show Cost"); |
| 154 | + if(showCost) { |
| 155 | + plots.plotCost(climateModel, discountRate, spec); |
| 156 | + } |
| 157 | + else { |
| 158 | + plots.closeCost(); |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + @Override |
| 165 | + protected void onClose() { |
| 166 | + synchronized(this) { |
| 167 | + super.onClose(); |
| 168 | + |
| 169 | + if(plots != null) plots.close(); |
| 170 | + } |
| 171 | + } |
| 172 | +} |
0 commit comments