Skip to content

Commit 15ac23b

Browse files
author
I800461
committed
misc
1 parent 61009a9 commit 15ac23b

File tree

6 files changed

+226
-44
lines changed

6 files changed

+226
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package rtss.data.curves;
2+
3+
import rtss.util.Util;
4+
5+
/*
6+
* Деформировать "носик" кривой l(x) с дневным разрешением в пределах первого года.
7+
* Использовать полином 4-й степени.
8+
*/
9+
public class SculptDailyLX
10+
{
11+
public static double[] scultDailyLX(double[] f, double convexityControl) throws Exception
12+
{
13+
int x1 = 365;
14+
15+
// Clone the original array
16+
double[] fClone = f.clone();
17+
18+
// Boundary conditions
19+
double f0 = f[0]; // f(0)
20+
double fx1 = f[x1]; // f(x1)
21+
double dfx1 = (f[x1 + 1] - f[x1]) / 1.0; // Approximate f'(x1)
22+
double d2fx1 = (f[x1 + 1] - 2 * f[x1] + f[x1 - 1]) / 1.0; // Approximate f''(x1)
23+
24+
// Coefficients of the quartic polynomial f(x) = ax^4 + bx^3 + cx^2 + dx + e
25+
double e = f0; // From f(0) = f0
26+
27+
// Solve the system of equations for a, b, c, d:
28+
// 1. a*x1^4 + b*x1^3 + c*x1^2 + d*x1 + e = fx1
29+
// 2. 4a*x1^3 + 3b*x1^2 + 2c*x1 + d = dfx1
30+
// 3. 12a*x1^2 + 6b*x1 + 2c = d2fx1
31+
// 4. a = convexityControl (extra condition to control convexity)
32+
33+
double a = convexityControl; // Control parameter for convexity
34+
35+
// Substitute a into the equations and solve for b, c, d
36+
double[][] A = {
37+
{ x1 * x1 * x1, x1 * x1, x1 },
38+
{ 3 * x1 * x1, 2 * x1, 1 },
39+
{ 6 * x1, 2, 0 }
40+
};
41+
42+
double[] B = {
43+
fx1 - e - a * x1 * x1 * x1 * x1,
44+
dfx1 - 4 * a * x1 * x1 * x1,
45+
d2fx1 - 12 * a * x1 * x1
46+
};
47+
48+
// Solve the system Ax = B for x = [b, c, d]
49+
double[] coefficients = solveLinearSystem(A, B);
50+
double b = coefficients[0];
51+
double c = coefficients[1];
52+
double d = coefficients[2];
53+
54+
// Fill in the missing values
55+
for (int x = 1; x < x1; x++)
56+
{
57+
fClone[x] = a * x * x * x * x + b * x * x * x + c * x * x + d * x + e;
58+
}
59+
60+
validate(Util.splice(fClone, 0, x1 + 100));
61+
62+
return fClone;
63+
}
64+
65+
// Helper method to solve a 3x3 linear system Ax = B
66+
private static double[] solveLinearSystem(double[][] A, double[] B)
67+
{
68+
int n = B.length;
69+
double[] x = new double[n];
70+
71+
// Using Gaussian elimination
72+
for (int i = 0; i < n; i++)
73+
{
74+
// Pivot for the current row
75+
double pivot = A[i][i];
76+
for (int j = i + 1; j < n; j++)
77+
{
78+
double factor = A[j][i] / pivot;
79+
B[j] -= factor * B[i];
80+
for (int k = i; k < n; k++)
81+
{
82+
A[j][k] -= factor * A[i][k];
83+
}
84+
}
85+
}
86+
87+
// Back substitution
88+
for (int i = n - 1; i >= 0; i--)
89+
{
90+
x[i] = B[i];
91+
for (int j = i + 1; j < n; j++)
92+
{
93+
x[i] -= A[i][j] * x[j];
94+
}
95+
x[i] /= A[i][i];
96+
}
97+
98+
return x;
99+
}
100+
101+
private static void validate(double[] f) throws Exception
102+
{
103+
double[] d1 = derivative(f);
104+
double[] d2 = derivative(d1);
105+
106+
if (!Util.isMonotonicallyDecreasing(f, true))
107+
throw new Exception("Improperly scuplted lx (non-decreasing)");
108+
109+
if (!Util.isNegative(d1))
110+
throw new Exception("Improperly scuplted lx (non-negative d1)");
111+
112+
if (!Util.isPositive(d2))
113+
throw new Exception("Improperly scuplted lx (non-positive d2)");
114+
}
115+
116+
private static double[] derivative(double[] p)
117+
{
118+
if (p.length <= 1)
119+
return new double[0];
120+
121+
double[] d = new double[p.length - 1];
122+
for (int i = 0; i <= p.length - 2; i++)
123+
d[i] = p[i + 1] - p[i];
124+
return d;
125+
}
126+
127+
private static double[] d2(double[] p)
128+
{
129+
return derivative(derivative(p));
130+
}
131+
}

population-projection/src/main/java/rtss/data/mortality/SingleMortalityTable.java

+63-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Map;
55

66
import rtss.data.curves.InterpolateYearlyToDailyAsValuePreservingMonotoneCurve;
7+
import rtss.data.population.struct.Population;
78
import rtss.util.FastUUID;
89
import rtss.util.Util;
910

@@ -15,7 +16,8 @@ public class SingleMortalityTable
1516
{
1617
private Map<Integer, MortalityInfo> m = new HashMap<>();
1718
private boolean sealed = false;
18-
public static final int MAX_AGE = 100;
19+
public static final int MAX_AGE = Population.MAX_AGE;
20+
public static final int DAYS_PER_YEAR = 365;
1921

2022
private SingleMortalityTable()
2123
{
@@ -452,12 +454,67 @@ public String toString()
452454

453455
/*****************************************************************************************************/
454456

457+
private Map<Integer, double[]> maxage2daily_lx = new HashMap<>();
458+
455459
/*
456-
* Построить кривую l(x) интерполированную по дням
460+
* Построить кривую l(x) интерполированную по дням с длиной до возраста MAX_AGE включительно
457461
*/
458462
public double[] daily_lx() throws Exception
459463
{
460-
double[] yearly_lx = lx();
464+
double[] daily_lx = maxage2daily_lx.get(MAX_AGE);
465+
466+
if (daily_lx == null)
467+
{
468+
double[] yearly_lx = lx();
469+
470+
/*
471+
* Провести дневную кривую так что
472+
* daily_lx[0] = yearly_lx[0]
473+
* daily_lx[365] = yearly_lx[1]
474+
* daily_lx[365 * 2] = yearly_lx[2]
475+
* etc.
476+
*/
477+
daily_lx = InterpolateYearlyToDailyAsValuePreservingMonotoneCurve.yearly2daily(yearly_lx);
478+
479+
/*
480+
* Базовая проверка правильности
481+
*/
482+
if (Util.differ(daily_lx[0], yearly_lx[0]) ||
483+
Util.differ(daily_lx[365 * 1], yearly_lx[1]) ||
484+
Util.differ(daily_lx[365 * 2], yearly_lx[2]) ||
485+
Util.differ(daily_lx[365 * 3], yearly_lx[3]))
486+
{
487+
throw new Exception("Ошибка в построении daily_lx");
488+
}
489+
490+
// может не спадать, если для какого-то года смертность (qx) нулевая, но это было бы невозможным
491+
Util.assertion(Util.isMonotonicallyDecreasing(daily_lx, true));
492+
493+
maxage2daily_lx.put(MAX_AGE, daily_lx);
494+
}
495+
496+
return daily_lx;
497+
}
498+
499+
/*
500+
* Построить кривую l(x) интерполированную по дням с длиной до возраста maxage включительно
501+
*/
502+
public double[] daily_lx(int maxage) throws Exception
503+
{
504+
if (maxage == MAX_AGE)
505+
return daily_lx();
506+
507+
double[] daily_lx = maxage2daily_lx.get(maxage);
508+
if (daily_lx != null)
509+
return daily_lx;
510+
511+
daily_lx = maxage2daily_lx.get(MAX_AGE);
512+
if (daily_lx != null)
513+
{
514+
daily_lx = Util.splice(daily_lx, 0, DAYS_PER_YEAR * (maxage + 1) - 1);
515+
maxage2daily_lx.put(maxage, daily_lx);
516+
return daily_lx;
517+
}
461518

462519
/*
463520
* Провести дневную кривую так что
@@ -466,7 +523,8 @@ public double[] daily_lx() throws Exception
466523
* daily_lx[365 * 2] = yearly_lx[2]
467524
* etc.
468525
*/
469-
double[] daily_lx = InterpolateYearlyToDailyAsValuePreservingMonotoneCurve.yearly2daily(yearly_lx);
526+
double[] yearly_lx = Util.splice(lx(), 0, maxage);
527+
daily_lx = InterpolateYearlyToDailyAsValuePreservingMonotoneCurve.yearly2daily(yearly_lx);
470528

471529
/*
472530
* Базовая проверка правильности
@@ -482,6 +540,7 @@ public double[] daily_lx() throws Exception
482540
// может не спадать, если для какого-то года смертность (qx) нулевая, но это было бы невозможным
483541
Util.assertion(Util.isMonotonicallyDecreasing(daily_lx, true));
484542

543+
maxage2daily_lx.put(maxage, daily_lx);
485544
return daily_lx;
486545
}
487546
}

population-projection/src/main/java/rtss/data/population/struct/PopulationContext.java

+2-39
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ public PopulationContext clone()
138138
cx.began = began;
139139
cx.hasRuralUrban = hasRuralUrban;
140140
cx.m = new LocalityGenderToDoubleArray(m);
141-
cx.m_lx = new HashMap<String, double[]>(m_lx);
142141
cx.totalBirths = new HashMap<>(totalBirths);
143142
cx.title = title;
144143
cx.yearHint = yearHint;
@@ -334,48 +333,13 @@ public int day2age(int nd)
334333

335334
/* =============================================================================================== */
336335

337-
private Map<String, double[]> m_lx = new HashMap<>();
338-
339336
/*
340337
* Вычислить подневное значение "lx" их годовых значений в таблице смертности
341338
*/
342339
public double[] get_daily_lx(final CombinedMortalityTable mt, final Locality locality, final Gender gender) throws Exception
343340
{
344-
String key = String.format("%s-%s-%s-%d", mt.tableId(), locality.name(), gender.name(), NDAYS);
345-
double[] daily_lx = m_lx.get(key);
346-
347-
if (daily_lx == null)
348-
{
349-
double[] yearly_lx = mt.getSingleTable(locality, gender).lx();
350-
/* значения после MAX_YEAR + 1 не слишком важны */
351-
yearly_lx = Util.splice(yearly_lx, 0, Math.min(MAX_YEAR + 5, Population.MAX_AGE));
352-
353-
/*
354-
* Провести дневную кривую так что
355-
* daily_lx[0] = yearly_lx[0]
356-
* daily_lx[365] = yearly_lx[1]
357-
* daily_lx[365 * 2] = yearly_lx[2]
358-
* etc.
359-
*/
360-
daily_lx = InterpolateYearlyToDailyAsValuePreservingMonotoneCurve.yearly2daily(yearly_lx);
361-
362-
/*
363-
* Базовая проверка правильности
364-
*/
365-
if (Util.differ(daily_lx[0], yearly_lx[0]) ||
366-
Util.differ(daily_lx[365 * 1], yearly_lx[1]) ||
367-
Util.differ(daily_lx[365 * 2], yearly_lx[2]) ||
368-
Util.differ(daily_lx[365 * 3], yearly_lx[3]))
369-
{
370-
throw new Exception("Ошибка в построении daily_lx");
371-
}
372-
373-
m_lx.put(key, daily_lx);
374-
}
375-
376-
Util.assertion(Util.isMonotonicallyDecreasing(daily_lx, true));
377-
378-
return daily_lx;
341+
/* значения после MAX_YEAR + 1 не слишком важны */
342+
return mt.getSingleTable(locality, gender).daily_lx(Math.min(MAX_YEAR + 5, Population.MAX_AGE));
379343
}
380344

381345
/* =============================================================================================== */
@@ -1317,7 +1281,6 @@ public PopulationContext toTotal() throws Exception
13171281
cx.valueConstraint = valueConstraint;
13181282
cx.began = began;
13191283
cx.hasRuralUrban = false;
1320-
cx.m_lx = new HashMap<String, double[]>(m_lx);
13211284
cx.totalBirths = new HashMap<>(totalBirths);
13221285
cx.title = title;
13231286
cx.yearHint = yearHint;

rtss-runtime/src/main/java/rtss/util/Util.java

+11
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,17 @@ public static boolean isNonNegative(final double[] yy)
645645
return true;
646646
}
647647

648+
public static boolean isNegative(final double[] yy)
649+
{
650+
for (double y : yy)
651+
{
652+
if (y >= 0)
653+
return false;
654+
}
655+
656+
return true;
657+
}
658+
648659
public static boolean isMonotonicallyDecreasing(final double[] y, boolean strict)
649660
{
650661
if (y.length == 0)

ww2-losses/src/main/java/rtss/ww2losses/helpers/diag/DiagHelper.java

+18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import static rtss.data.population.projection.ForwardPopulation.years2days;
1010

11+
import rtss.data.curves.SculptDailyLX;
1112
import rtss.data.mortality.CombinedMortalityTable;
1213
import rtss.data.population.struct.PopulationContext;
1314
import rtss.data.selectors.Gender;
@@ -112,6 +113,8 @@ public static void viewProjection(PopulationContext p, PeacetimeMortalityTables
112113

113114
CombinedMortalityTable cmt = peacetimeMortalityTables.getTable(1941, HalfYearSelector.FirstHalfYear);
114115
double[] lx = peacetimeMortalityTables.mt2lx(1941, HalfYearSelector.FirstHalfYear, cmt, Locality.TOTAL, gender);
116+
// viewLX(lx);
117+
Clipboard.put(lx);
115118
double[] survival = lx2survival(lx, ndays);
116119
viewProjection(a, survival, ndays);
117120
}
@@ -127,4 +130,19 @@ public static void view_mid1941(PopulationContext p, Gender gender) throws Excep
127130
Clipboard.put(sb.toString());
128131
Util.noop();
129132
}
133+
134+
@SuppressWarnings("unused")
135+
private static void viewLX(double[] lx) throws Exception
136+
{
137+
ChartXY chart = new ChartXY("lx", "days", "lx");
138+
chart.addSeries("original", lx);
139+
140+
chart.addSeries("0", SculptDailyLX.scultDailyLX(lx, 0));
141+
chart.addSeries("1e-6", SculptDailyLX.scultDailyLX(lx, 1e-6));
142+
chart.addSeries("1.2e-6", SculptDailyLX.scultDailyLX(lx, 1.2e-6));
143+
144+
chart.display();
145+
146+
Util.noop();
147+
}
130148
}

ww2-losses/src/main/java/rtss/ww2losses/population1941/PopulationMiddle1941.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public PopulationForwardingResult1941 forward_1941_1st_halfyear(
8181
double[] m_births = WarHelpers.male_births(births);
8282
double[] f_births = WarHelpers.female_births(births);
8383

84-
if (Util.False)
84+
if (Util.True)
8585
DiagHelper.viewProjection(p_start1941.clone(), peacetimeMortalityTables, Gender.MALE, ndays);
8686

8787
fw = new ForwardPopulationT();

0 commit comments

Comments
 (0)