Using Josh Spreadsheet for input

Needs["Pensions`"]

baseDirectory=(*"/Users/sethchandler/Dropbox/Novim Pensions";*)"/Users/sethchandler/Documents/TemporaryPensions/";

commonDirectory=(*"/Users/sethchandler/Dropbox/Pensions/3 - Modeling";*)"/Users/sethchandler/Documents/TemporaryPensions/";

notWorkingAnymoreHazard[survival_,survivalVectorInitialAge_,separationHazardRaggedRight_,initialEntryAge_,entryAge_]:=balancedMultipleDecrementHazardVectorC[survivalToHazardC[truncateSurvivalVectorC3[survival,survivalVectorInitialAge,entryAge]],separationHazardRaggedRight[[1+entryAge-initialEntryAge]]]

fc35=Import[FileNameJoin[{commonDirectory,"TRS Pension Wealth Accrual (JM 04162013).xlsx"}],{"Sheets","Female Calc 35yr Old"}];

probs35=Rest[fc35[[5;;45,38]]]

npw35=fc35[[5;;45,14]]

haz35=probsToHazardC[probs35]

sal35=fc35[[5;;45,3]]

dnpw35=fc35[[5;;45,14]]*discreteDiscountTableC[0.05,0,41]

normalCostPercentageFromRawDataC[haz35,{sal35,dnpw35},0.05,{1,1}]

fc25=Import[FileNameJoin[{commonDirectory,"TRS Pension Wealth Accrual (JM 04162013).xlsx"}],{"Sheets","Female Calc"}];

probs25=Rest[fc25[[5;;55,38]]]

npw25=fc25[[5;;55,14]]

haz25=probsToHazardC[probs25]

sal25=fc25[[5;;55,3]]

dnpw25=fc25[[5;;55,14]]*discreteDiscountTableC[0.05,0,51]

normalCostPercentageFromRawDataC[haz25,{sal25,dnpw25},0.05,{1,1}]

Using Mathematica to generate benefit side

Constants and functions

Needs["Pensions`"]

baseDirectory=(*"/Users/sethchandler/Dropbox/Novim Pensions";*)"/Users/sethchandler/Documents/TemporaryPensions/";

commonDirectory=(*"/Users/sethchandler/Dropbox/Pensions/3 - Modeling";*)"/Users/sethchandler/Documents/TemporaryPensions/";

notWorkingAnymoreHazard[survival_,survivalVectorInitialAge_,separationHazardRaggedRight_,initialEntryAge_,entryAge_]:=balancedMultipleDecrementHazardVectorC[survivalToHazardC[truncateSurvivalVectorC3[survival,survivalVectorInitialAge,entryAge]],separationHazardRaggedRight[[1+entryAge-initialEntryAge]]]

AntiDiagonalUpperTriangularToUpperTriangular=Compile[{{m,_Real,2}},MapIndexed[{row,index}|->With[{i=index[[1]]-1},RotateRight[row,i]],m],CompilationTarget->"C",CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"]

UpperTriangularToAntiDiagonalUpperTriangular=Compile[{{m,_Real,2}},MapIndexed[{row,index}|->With[{i=index[[1]]-1},RotateLeft[row,i]],m],CompilationTarget->"C",CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"]

workforceEvolveStep=Compile[{{persistence,_Real,2},{workforceNow,_Real,2},{distribution,_Real,1},{growth,_Real}},
Module[{totalNow=Total[Flatten[workforceNow]],newBeforeRotation,totalNext,newHires},
(*Assert[Total[distribution]]==1;*)
newBeforeRotation=persistence*workforceNow;
totalNext=Total[Flatten[newBeforeRotation]];
newHires=((1+growth)totalNow-totalNext)*distribution;
DiagonalMatrix[newHires]+Map[row|->RotateRight[row],persistence*workforceNow]
],CompilationTarget->"C",CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

workforceEvolve=Compile[{{persistence,_Real,2},{workforceNow,_Real,2},{distribution,_Real,1},{growth,_Real},{steps,_Integer}},
NestList[workforce|->workforceEvolveStep[persistence,workforce,distribution,growth],workforceNow,steps],CompilationTarget->"C",
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

Data entry

entryAge=25;

serviceDomain={0,55};

retirementDomain={20,75};

survivalTableInitialAge=20;

killOffAge=120;

discountRate=0.05;

normalCostDiscountRate=0.05 (* check with Josh on this *);

COLARate=0.;

inflation=0.025;

fasBasisYears=5;

employeeContributionRatesList={{0,1000000,0.064}};

employerContributionRatesList={{0,1000000,0}};

ϕ=1.;

survival={1.`,0.999878`,0.9997590145180001`,0.9996390434362579`,0.9995140885558282`,0.9993821526961388`,0.9992432385769141`,0.9990903543614119`,0.9989314989950684`,0.9987636785032372`,0.9985868973321421`,0.9983881785395731`,0.9981435734358308`,0.9978640932352688`,0.9975587468227388`,0.997231547553781`,0.9968845109752322`,0.9965186543597043`,0.9961359911964301`,0.995734548391978`,0.9953123569434598`,0.9948525226345519`,0.9943481324055762`,0.9937932861476939`,0.9931830970699992`,0.9925136916625741`,0.9917980892908854`,0.9909897738481134`,0.9900423876243146`,0.9889087890904849`,0.987543106052751`,0.9858998343242792`,0.9841439467193476`,0.982178611257749`,0.9799451370957488`,0.977388460233066`,0.9744494531331452`,0.9710583690362419`,0.967206180486275`,0.9629001785707502`,0.9580827889773608`,0.9527089026139868`,0.9467458975925258`,0.9401603331288723`,0.9329239190447793`,0.9249949986568178`,0.91632317054441`,0.9068630501317095`,0.8965755956910154`,0.8854069534954924`,0.8732697949769761`,0.8600441239320499`,0.8458774771226412`,0.8303793099868002`,0.8138057693387737`,0.7958035719152308`,0.7767560134214397`,0.7562721805915029`,0.7338600545196737`,0.7098870481186795`,0.6842842618412313`,0.6570045854586688`,0.6280149151298905`,0.5972949375413968`,0.5648540575987109`,0.5307340482494607`,0.4942917255604597`,0.45566925300034206`,0.41515296570056365`,0.3740374614364769`,0.33206858807599704`,0.2908914190173973`,0.25128044359837926`,0.21398389247684624`,0.17894531398711752`,0.14751822216344398`,0.11995075439665039`,0.09628231144061213`,0.07596905450211754`,0.05929308734835772`,0.045845237258488145`,0.035174162298490665`,0.026562331446302`,0.019802271217881032`,0.014533995773991093`,0.010478211583280007`,0.0074068801168353056`,0.005126960941192113`,0.0034723624714458885`,0.0023006450067187163`,0.0014918670584767918`,0.0009479069672161595`,0.0005912607624289482`,0.0003630252392199378`,0.00022017226641021773`,0.00013247589132089674`,0.00007948553479253804`,0.00004769132087552282`,0.000028614792525313693`,0.000017168875515188216`,0.00001030132530911293`};(*Import["/Users/sethchandler/Dropbox/pensions/3 - Modeling/TRS Pension Wealth Accrual (JM 04162013).xlsx",{"Sheets","Female Actuarial Table"}][[3;;-1,3]]*)

multipleHazardTX=Module[{TXseparationTableRaw=Import[FileNameJoin[{commonDirectory,"TRS Dec Table.xlsx"}],{"Sheets","TRS Decrement Table"}],TXseparationTableRaw2,TXseparationTableRaw3,TXseparationTableRaw4},
TXseparationTableRaw2=Drop[TXseparationTableRaw,1,1];
TXseparationTableRaw3=Join[Table[TXseparationTableRaw2[[1]],{5}],TXseparationTableRaw2];
TXseparationTableRaw4=MapIndexed[PadRight[#,56+1-#2,1.]&,Replace[TXseparationTableRaw3,{a___?NumericQ,b_?(Not[NumericQ[#]]&),c___?(Not[NumericQ[#]]&)}:>{a,1},{1}]];
Table[ReplacePart[PadLeft[notWorkingAnymoreHazard[survival,20,TXseparationTableRaw4,20,entryAge],56],-1->1],{entryAge,20,75}]
];

MatrixPlot[multipleHazardTX,PlotLegends->Automatic]

txReplRateImage=;

txReplRate=ImageData[txReplRateImage];

salaryScale={44130.`,44130.`,44130.`,44233.`,44336.`,44439.`,44542.`,44748.`,44748.`,45057.`,45366.`,45675.`,46293.`,46911.`,47529.`,48147.`,48765.`,49383.`,50001.`,50619.`,51237.`,51855.`,52473.`,53091.`,53709.`,54327.`,54945.`,55563.`,56181.`,57417.`,58035.`,58653.`,59889.`,59889.`,60507.`,61125.`,62361.`,62919.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`,64833.`};(*Import["/Users/sethchandler/Dropbox/pensions/3 - Modeling/TRS Pension Wealth Accrual (JM 04162013).xlsx",{"Sheets","sal grid"}][[2;;-1,2]]*)

wfImage=;

workforceSnapshot=ImageData[wfImage];

workforceReplacementDistribution=With[{hiresThisYear=Diagonal[workforceSnapshot]},N[(hiresThisYear/(Total[hiresThisYear]))]]

Back to main line

bc[salaryScale_,fasBasisYears_,survival_,survivalTableInitialAge_,discountRate_,inflation_,replacementRateTable_,basedExclusivelyOnRetirementQ_,compoundQ_,COLARate_,lastSomethingWithoutIncrement_,COLACap_,killOffAge_,employeeContributionRatesList_,employerContributionRatesList_,serviceDomain_,retirementDomain_,person:{entryAge_,yos_}]:=Module[{age=entryAge+yos,mmcd=matrixMatrixCommonDomainC[{{serviceDomain,retirementDomain},{{0,retirementDomain[[2]]-entryAge},retirementDomain}}],eec,erc,αRR,αSDAF,αΩ,retirementAgeIndexStar,bim,z,salary,finalAverageSalary,cumulativeInvestedContributions,refundRight},
salary=Take[salaryVectorFromScaleEntryAgeAndYOSC[salaryScale,inflation,{entryAge,yos}],1+retirementDomain[[2]]-entryAge];
eec=contributionsC[salary,employeeContributionRatesList];
erc=contributionsC[salary,employerContributionRatesList];
αRR=trimMatrixByCommonDomainC[replacementRateTable,{serviceDomain,retirementDomain},mmcd];
αSDAF=trimMatrixByCommonDomainC[sDAFMatrixC[afRetirementAgeVectorC[survival,survivalTableInitialAge,discountRate,colaMatrixC[basedExclusivelyOnRetirementQ,compoundQ,COLARate,lastSomethingWithoutIncrement,COLACap,retirementDomain,killOffAge],retirementDomain],survivalDiscountRetirementAgeMatrixC[survival,survivalTableInitialAge,discountRate,{entryAge,retirementDomain[[2]]},retirementDomain]],{{0,retirementDomain[[2]]-entryAge},retirementDomain},mmcd];

αΩ=omegaC[αRR,αSDAF];
retirementAgeIndexStar=bestPositionsInMatrixByRowC@αΩ;
z=retirementRRSDAFΩStar[αRR,αSDAF,retirementDomain];

finalAverageSalary=fasVectorC[salary,fasBasisYears];
cumulativeInvestedContributions=Most@cumulativeInvestedFlowsC[eec,discountRate];
refundRight=Most@refundRightC[eec,erc,1,discountRate];
benefitSideC[z,salary,finalAverageSalary,cumulativeInvestedContributions,refundRight,inflation]
]

bothSides[salaryScale_,fasBasisYears_,survival_,survivalTableInitialAge_,discountRate_,inflation_,replacementRateTable_,basedExclusivelyOnRetirementQ_,compoundQ_,COLARate_,lastSomethingWithoutIncrement_,COLACap_,killOffAge_,employeeContributionRatesList_,employerContributionRatesList_,serviceDomain_,retirementDomain_,person:{entryAge_,yos_},notWorkingHazard_,discountRateForEmployer_,paymentTimeRelativeToHazardSalary_:1,paymentTimeRelativeToHazardPension_:1]:=Module[{age=entryAge+yos,mmcd=matrixMatrixCommonDomainC[{{serviceDomain,retirementDomain},{{0,retirementDomain[[2]]-entryAge},retirementDomain}}],eec,erc,αRR,αSDAF,αΩ,retirementAgeIndexStar,bim,z,salary,finalAverageSalary,cumulativeInvestedContributions,refundRight},
salary=Take[salaryVectorFromScaleEntryAgeAndYOSC[salaryScale,inflation,{entryAge,yos}],1+retirementDomain[[2]]-entryAge];
eec=contributionsC[salary,employeeContributionRatesList];
erc=contributionsC[salary,employerContributionRatesList];
αRR=trimMatrixByCommonDomainC[replacementRateTable,{serviceDomain,retirementDomain},mmcd];
αSDAF=trimMatrixByCommonDomainC[sDAFMatrixC[afRetirementAgeVectorC[survival,survivalTableInitialAge,discountRate,colaMatrixC[basedExclusivelyOnRetirementQ,compoundQ,COLARate,lastSomethingWithoutIncrement,COLACap,retirementDomain,killOffAge],retirementDomain],survivalDiscountRetirementAgeMatrixC[survival,survivalTableInitialAge,discountRate,{entryAge,retirementDomain[[2]]},retirementDomain]],{{0,retirementDomain[[2]]-entryAge},retirementDomain},mmcd];

αΩ=omegaC[αRR,αSDAF];
retirementAgeIndexStar=bestPositionsInMatrixByRowC@αΩ;
z=retirementRRSDAFΩStar[αRR,αSDAF,retirementDomain];

finalAverageSalary=fasVectorC[salary,fasBasisYears];
cumulativeInvestedContributions=Most@cumulativeInvestedFlowsC[eec,discountRate];
refundRight=Most@refundRightC[eec,erc,1,discountRate];
benefitAndCostSideSingleStepC[z,salary,finalAverageSalary,cumulativeInvestedContributions,refundRight,inflation,notWorkingHazard,discountRateForEmployer,paymentTimeRelativeToHazardSalary,paymentTimeRelativeToHazardPension]
]

simpleUserInterfaceBothSides[colaRate_,deflateForInflation_,discountRate_,employeeContributionRate_,entryAge_,fasYears_,inflation_,showTooltips_]:=Module[{data$=bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1],tips$},tips$=Transpose[data$[[{1,2,3,4,5,8,9,10,-2,-1}]]];Labeled[ListLinePlot[If[showTooltips,MapThread[Function[{datum,tip},Tooltip[datum,Grid[Transpose[{{"salary","final average salary","cumulative invested contributions","refund right","optimal retirement age","optimal replacement rate","first year payment","present  value of annuity","normal cost percentage","normal cost"},tip}],Dividers->All,BaseStyle->{FontSize->11,LineIndent->0},ItemSize->15]]],{If[deflateForInflation,data$[[{-5,-4}]],data$[[{-7,-6}]]],{tips$,tips$}},2],If[deflateForInflation,data$[[{-5,-4}]],data$[[{-7,-6}]]]],Axes->False,Frame->True,FrameLabel->{"years of service","$"},PlotLegends->{Style[If[deflateForInflation,"deflated ",""]<>"accrued pension wealth",{FontSize->14,FontFamily->"Swiss"}],Style[If[deflateForInflation,"deflated ",""]<>"accrued net pension wealth",{FontSize->14,FontFamily->"Swiss"}]},PlotStyle->Thick,BaseStyle->{FontSize->14,FontFamily->"Swiss"},PlotMarkers->{Automatic,Tiny},ImageSize->500,ImagePadding->{{80,10},{50,10}}],Row[{"Normal cost percentage is ",data$[[-2,1]]},BaseStyle->{Background->Lighter@ColorData[1][3],FontSize->14,FontFamily->"Swiss"}]]]

Manipulate[simpleUserInterfaceBothSides[colaRate,deflateForInflation,discountRate,employeeContributionRate,entryAge,fasYears,inflation,showTooltips],
{{showTooltips,False,"provide data tooltips"},{True,False}},
{{deflateForInflation,True,"show deflated values to take account of inflation"},{True,False}},
Row[{"initial pension annuity is replacement rate multiplied by the average salary for the final ... ",Control@{{fasYears,5,""},Range[7],ControlType->SetterBar},"... years"}],
{{discountRate,0.05,"discount rate"},0.01,0.1,0.005,Appearance->"Labeled"},
{{inflation,0.025,"inflation rate"},0.,0.1,0.005,Appearance->"Labeled"},
{{entryAge,25,"entry age"},20,60,1,Appearance->"Labeled"},
{{colaRate,0,"COLA Rate"},0,0.1,0.005,Appearance->"Labeled"},
{{employeeContributionRate,0.064,"employee contribution rate"},0.0,0.08,0.001,Appearance->"Labeled"}]

DynamicModule[{colaRate=0,deflateForInflation=True,discountRate=0.055`,employeeContributionRate=0.064`,entryAge=29,fasYears=3,inflation=0.025`,showTooltips=True},simpleUserInterfaceBothSides[colaRate,deflateForInflation,discountRate,employeeContributionRate,entryAge,fasYears,inflation,showTooltips]]

Stuff unlikely to show

Table[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],0.05,1,1],{ea,46,51,5}][[2]]

Labeled[ListLinePlot[With[{t=Table[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],0.05,1,1],{ea,20,75,5}]},
Map[Thread[List[Range[1+75-Length[#],75],#]]&,Part[t,All,-4]]],Axes->False,Frame->True,FrameLabel->{"age","value"},ImageSize->600,PlotRange->All,Joined->True,PlotLegends->Range[20,75,5],ImageSize->400,PlotStyle->Thick,BaseStyle->{FontFamily->"Swiss",FontSize->16}],
Style["Deflated Net Pension Wealth Trajectories\nAs A Function of Entry Age",{FontFamily->"Swiss",FontSize->16}]
]

Labeled[ListLinePlot[With[{t=Table[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],0.05,1,1],{ea,20,75,5}]},
Map[Thread[List[Range[1+75-Length[#],75],#]]&,Part[t,All,-1]]],Axes->False,Frame->True,FrameLabel->{"age","value"},ImageSize->600,PlotRange->All,Joined->True,PlotLegends->Range[20,75,5],ImageSize->400,PlotStyle->Thick,BaseStyle->{FontFamily->"Swiss",FontSize->16}],
Style["Normal Cost Trajectories\nAs A Function of Entry Age",{FontFamily->"Swiss",FontSize->16}]
]

Manipulate[With[{t=Table[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],0.05,1,1],{ea,20,75,1}],labels={"salary","FAS","cumulative invested contributions","refund right","retirement age","Ωstar","SDAFStar","Replacement Rate Star","first year annuity payments star","pv annuity as of separation star","annuityQ","pension wealth","net pension wealth","pension wealth deflated","net pension wealth deflated","discounted net pension wealth","normal cost %","normal cost"}},Labeled[f[MapIndexed[Tooltip[Thread[List[Range[1+75-Length[#],75],#]],Range[20,75,1][[#2[[1]]]]]&,Part[t,All,part]],Axes->False,Frame->True,FrameLabel->{"age","value"},ImageSize->600,PlotRange->All,Joined->True,PlotLegends->Range[20,75,1],ImageSize->400,BaseStyle->{FontFamily->"Swiss",FontSize->16}],labels[[part]]]
],{part,1,18,1},{f,{ListLinePlot,ListLogPlot}}]

Onwards to aggregate cost trajectories

Manipulate[Labeled[MatrixPlot[Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-1]],56],{entryAge,20,75,1}],FrameTicks->{Table[{i,i+20},{i,0,55,5}],Table[{i+1,i},{i,0,55,5}]},FrameLabel->{"entry age","complete years of service"},Mesh->{Range[0,55,5],Range[0,55,5]},BaseStyle->{FontSize->12,FontFamily->"Swiss"}],Style["Normal cost by entry age and completed years of service",{FontSize->12,FontFamily->"Swiss"}]],Row[{"initial pension annuity is replacement rate multiplied by the average salary for the final ... ",Control@{{fasYears,5,""},Range[7],ControlType->SetterBar},"... years"}],
{{discountRate,0.05,"discount rate"},0.01,0.1,0.005,Appearance->"Labeled"},
{{inflation,0.025,"inflation rate"},0.,0.1,0.005,Appearance->"Labeled"},
{{colaRate,0,"COLA Rate"},0,0.1,0.005,Appearance->"Labeled"},
{{employeeContributionRate,0.064,"employee contribution rate"},0.0,0.08,0.001,Appearance->"Labeled"},SynchronousUpdating->False
]

Module[{colaRate=0,discountRate=0.05`,employeeContributionRate=0.064`,fasYears=5,inflation=0.025`},Labeled[MatrixPlot[AntiDiagonalUpperTriangularToUpperTriangular[Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-1]],56],{entryAge,20,75,1}]],FrameTicks->{Table[{i,i+20},{i,0,55,5}],Table[{i,i+20},{i,0,55,5}]},FrameLabel->{"entry age","age"},Mesh->{Range[0,55,5],Range[0,55,5]},BaseStyle->{FontSize->12,FontFamily->"Swiss"}],Style["Normal cost by entry age and completed years of service",{FontSize->12,FontFamily->"Swiss"}]]
]

Module[{colaRate=0,discountRate=0.05`,employeeContributionRate=0.064`,fasYears=5,inflation=0.025`},Labeled[MatrixPlot[AntiDiagonalUpperTriangularToUpperTriangular[Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[1]],56],{entryAge,20,75,1}]],FrameTicks->{Table[{i,i+20},{i,0,55,5}],Table[{i,i+20},{i,0,55,5}]},FrameLabel->{"entry age","age"},Mesh->{Range[0,55,5],Range[0,55,5]},BaseStyle->{FontSize->12,FontFamily->"Swiss"}],Style["Salary by entry age and completed years of service",{FontSize->12,FontFamily->"Swiss"}]]
]

Timing[Grid[𝒞=Table[PadRight[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],discountRate,1,1][[-1]],56],{ea,20,75,1}],BaseStyle->6]];

Labeled[MatrixPlot[workforceSnapshot,FrameTicks->{Table[{i+1,i+20},{i,0,55,5}],Table[{i+1,i+20},{i,0,55,5}]},FrameLabel->{"entry age","age"},BaseStyle->{FontSize->12,FontFamily->"Swiss"}],Style["entry age and age distribution of Texas teachers",{FontSize->12,FontFamily->"Swiss"}]
]

Magnify[Row[{Labeled[MatrixPlot[workforceSnapshot,FrameTicks->None],Style["workforce",10]],"×",Labeled[MatrixPlot[UpperTriangularize@(1-multipleHazardTX),FrameTicks->None],Style["persistence",10]],"=",Labeled[MatrixPlot[workforceSnapshot×UpperTriangularize@(1-multipleHazardTX),FrameTicks->None],Style["new workforce\nbefore aging and replacement",10]]},Spacer[12]],1.5]

Magnify[Row[{Labeled[MatrixPlot[workforceSnapshot,FrameTicks->None],Style["workforce",10]],"×",Labeled[MatrixPlot[UpperTriangularize@(1-multipleHazardTX),FrameTicks->None],Style["persistence",10]],"=",Labeled[MatrixPlot[Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]],FrameTicks->None,PlotLabel->"rotated right"],Style["new workforce\nafter aging before replacement",10]]},Spacer[12]],1.5]

ListLinePlot[(Total[Flatten[workforceSnapshot]]*1.03-Total[Flatten[Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]]]])*((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]])),DataRange->{20,75},PlotStyle->Thick,PlotMarkers->Automatic,Axes->False,Frame->True,FrameLabel->{"age","new workers"},BaseStyle->{FontSize->14,FontFamily->"Swiss"},ImageSize->700]

Magnify[Row[{Labeled[MatrixPlot[DiagonalMatrix[(Total[Flatten[workforceSnapshot]]*1.03-Total[Flatten[Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]]]])*((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]]))],FrameTicks->None],"new workers"],"+",Labeled[MatrixPlot[Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]],FrameTicks->None,PlotLabel->"rotated right"],Style["new workforce\nafter aging before replacement",10]],"=",Labeled[MatrixPlot[DiagonalMatrix[(Total[Flatten[workforceSnapshot]]*1.03-Total[Flatten[Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]]]])*((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]]))]+Transpose@ReplacePart[Transpose@Map[RotateRight,workforceSnapshot×UpperTriangularize@(1-multipleHazardTX)],1->ConstantArray[0,56]],FrameTicks->None],"new workforce"]},Spacer[6]],1.5]

Labeled[MatrixPlot[UpperTriangularize@(1-multipleHazardTX),FrameTicks->{Table[{i+1,i+20},{i,0,55,5}],Table[{i+1,i+20},{i,0,55,5}]},FrameLabel->{"entry age","age"},BaseStyle->{FontSize->12,FontFamily->"Swiss"}],Style["persistence of Texas teachers",{FontSize->12,FontFamily->"Swiss"}]
]

Module[{tensor=workforceEvolve[1-multipleHazardTX,workforceSnapshot,((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]])),0.03,20],max},max=Max[Flatten[tensor]];
Image3D[workforceTensor=tensor/max,Axes->True,AxesLabel->{"entry age","age","future"},ImageSize->600]]

𝒞=Table[PadRight[bothSides[salaryScale,fasBasisYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.,1000,killOffAge,employeeContributionRatesList,employerContributionRatesList,serviceDomain,retirementDomain,{ea,0},multipleHazardTX[[ea-19]],discountRate,1,1][[-1]],56],{ea,20,75,1}];

wh=workforceEvolve[1-multipleHazardTX,workforceSnapshot,((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]])),0.0,50];

notAdjustedForInflationCostTensor=Compile[{{workforceHistory,_Real,3},{costMatrix,_Real,2}},MapIndexed[{workforce,index}|->With[{t=index[[1]]-1},UpperTriangularToAntiDiagonalUpperTriangular[workforce]*costMatrix],workforceHistory],CompilationTarget->"C",
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

generateTensorIndices=Compile[{{costMatrixDimensions,_Integer,1},{yearsToProject,_Integer}},Outer[List,Range@costMatrixDimensions[[1]],Range@costMatrixDimensions[[2]],Range@yearsToProject],CompilationTarget->"C",
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

adjustedForInflationCostTrajectory=Compile[{{unadjustedCostTensor,_Real,3},{inflation,_Real}},
Module[{dims=Dimensions[unadjustedCostTensor],indices},
indices=generateTensorIndices[Take[dims,-2],dims[[1]]];
Map[Total[Flatten[#]]&,Take[MapThread[#1*((1+inflation)^(#2[[3]]+#2[[1]]-#2[[2]]))&,{unadjustedCostTensor,indices},3],Length[unadjustedCostTensor]]
]
],
CompilationTarget->"C",
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

costTrajectory=Compile[{{workforceHistory,_Real,3},{costMatrix,_Real,2},{inflation,_Real}},
With[{notAdjusted=notAdjustedForInflationCostTensor[workforceHistory,costMatrix]},
adjustedForInflationCostTrajectory[notAdjusted,inflation]
],
CompilationTarget->"C",
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->"Speed"
];

costTrajectory[wh,𝒞,0.01]

Row[{Labeled[MatrixPlot[AntiDiagonalUpperTriangularToUpperTriangular[Module[{fasYears=5,colaRate=0.,employeeContributionRate=0.064},Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-1]],56],{entryAge,20,75,1}]]
],FrameTicks->None],"normal cost matrix"],"×",Labeled[MatrixPlot[workforceTensor[[14]],FrameTicks->None],"workforce distribution\n
in year 13"]},Spacer[12]]

With[{m=AntiDiagonalUpperTriangularToUpperTriangular[Module[{fasYears=5,colaRate=0.,employeeContributionRate=0.064},Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-1]],56],{entryAge,20,75,1}]]
]},ListAnimate[MapIndexed[{matrix,index}|->Row[{Labeled[MatrixPlot[m,FrameTicks->None],"normal cost matrix"],"×",Labeled[MatrixPlot[matrix,FrameTicks->None],"workforce distribution\n
in year "<>ToString[index[[1]]-1]],Total[Flatten[m*matrix]]},Spacer[12]],workforceTensor]
]
]

Manipulate[Module[{yearsToForecast=50,workforceHistory,costMatrix,annualNormalCost,annualSalary,result},
costMatrix=Table[PadRight[Thread[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[{1,-1}]]],56,item[0.001,0]],{entryAge,20,75,1}];
workforceHistory=workforceEvolve[1-multipleHazardTX,workforceSnapshot,((Diagonal[workforceSnapshot])/(Total[Diagonal[workforceSnapshot]])),growthRate,yearsToForecast];
annualNormalCost=costTrajectory[workforceHistory,Map[Last,costMatrix,{2}],inflation];
annualSalary=costTrajectory[workforceHistory,Map[First,costMatrix,{2}],inflation];


result=annualNormalCost/annualSalary;
Labeled[ListPlot[Switch[plotWhat,"normal cost",annualNormalCost,"payroll",annualSalary,"normal cost percentage",result],
Joined->True,ImageSize->500,PlotMarkers->{Automatic,Tiny},Frame->True,Axes->False,FrameLabel->{"years into the future",plotWhat},GridLines->{Automatic,Automatic},ImagePadding->{{70,20},{60,10}},BaseStyle->{FontSize->14,FontFamily->"Swiss"}],
Style[plotWhat,{FontSize->14,FontFamily->"Swiss"}]]
],
{{plotWhat,"normal cost","plot what?"},{"normal cost","payroll","normal cost percentage"}},
Row[{"initial pension annuity is replacement rate multiplied by the average salary for the final ... ",Control@{{fasYears,5,""},Range[7],ControlType->SetterBar},"... years"}],
{{discountRate,0.05,"discount rate"},0.01,0.1,0.005,Appearance->"Labeled"},
{{inflation,0.025,"inflation rate"},0.,0.1,0.005,Appearance->"Labeled"},
{{colaRate,0,"COLA Rate"},0,0.1,0.005,Appearance->"Labeled"},
{{employeeContributionRate,0.064,"employee contribution rate"},0.0,0.08,0.001,Appearance->"Labeled"},
Delimiter,
{{growthRate,0,"rate of growth of workforce"},-0.02,0.04,0.001,Appearance->"Labeled"},
LabelStyle->{FontSize->14,FontFamily->"Swiss"}

]

Manipulate[Module[{take={56,105},result},

result=Take[ΤFromPrimitivesC[Table[PadRight[bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,colaRate,0.`,1000,killOffAge,{{0,1000000,employeeContributionRate}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-1]],56],{entryAge,20,75,1}],workforceSnapshot,multipleHazardTX,workforceReplacementDistribution,ConstantArray[growthRate,56],inflation],take];ListLogPlot[result,Joined->True,PlotMarkers->{Automatic,Tiny},PlotRange->{10^plotRange10PowerMin,10^(plotRange10PowerMin+plotRange10PowerDelta)},Frame->True,Axes->False,FrameLabel->{"years into the future","aggregate normal cost"},GridLines->{Automatic,Automatic},ImagePadding->{{40,40},{60,10}},BaseStyle->{FontSize->14,FontFamily->"Swiss"}]
],
Row[{"initial pension annuity is replacement rate multiplied by the average salary for the final ... ",Control@{{fasYears,5,""},Range[7],ControlType->SetterBar},"... years"}],
{{discountRate,0.05,"discount rate"},0.01,0.1,0.005,Appearance->"Labeled"},
{{inflation,0.025,"inflation rate"},0.,0.1,0.005,Appearance->"Labeled"},
{{colaRate,0,"COLA Rate"},0,0.1,0.005,Appearance->"Labeled"},
{{employeeContributionRate,0.064,"employee contribution rate"},0.0,0.08,0.001,Appearance->"Labeled"},
Delimiter,
{{growthRate,0,"rate of growth of workforce"},-0.02,0.04,0.001,Appearance->"Labeled"},
{{plotRange10PowerMin,6,"plot range min"},Range[4,10],ControlType->SetterBar},
{{plotRange10PowerDelta,4,"plot range max"},Range[1,6],ControlType->SetterBar}


]

Alternative separation data

maximumLengthSeparationHazard=ReplacePart[PadRight[{0.1938`,0.1685`,0.1438`,0.1263`,0.1075`,0.0935`,0.0825`,0.0724`,0.0564`,0.057`,0.057`,0.057`,0.057`,0.057`,0.057`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0459`,0.0402`,0.0402`,0.0402`,0.0402`,0.0402`,0.01`,0.01`,0.01`,0.01`,0.01`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.25`,0.75`},56,0.8],-1->1](*Round[probsToHazardC[Rest@Import["/Users/sethchandler/Dropbox/pensions/3 - Modeling/TRS Pension Wealth Accrual (JM 04162013).xlsx",{"Sheets","Female Calc"}][[5;;55,-4]]],0.00000001]*);

separationHazardRaggedRight=Table[ReplacePart[Drop[maximumLengthSeparationHazard,-i],-1->1],{i,0,55}];

notWorkingAnymoreHazard[survival_,survivalVectorInitialAge_,separationHazardRaggedRight_,initialEntryAge_,entryAge_]:=balancedMultipleDecrementHazardVectorC[survivalToHazardC[truncateSurvivalVectorC3[survival,survivalVectorInitialAge,entryAge]],separationHazardRaggedRight[[1+entryAge-initialEntryAge]]]

truncateSurvivalVectorC3[{1,1,1,1,1},20,23]

MatrixPlot[multipleHazard=Table[PadLeft[notWorkingAnymoreHazard[survival,20,separationHazardRaggedRight,20,entryAge],56],{entryAge,20,75}],PlotLegends->Automatic,FrameLabel->{"entry age","age"},FrameTicks->None]

MatrixPlot[multipleHazardWithoutMortality=Table[PadLeft[notWorkingAnymoreHazard[ConstantArray[1,101],20,separationHazardRaggedRight,20,entryAge],56],{entryAge,20,75}],PlotLegends->Automatic,FrameLabel->{"entry age","age"},FrameTicks->None]

Other materials for diagrams

bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[5]]//Length

entryAge

Labeled[With[{entryAge=34},ListLinePlot[DeleteCases[Thread[List[Range[34,75],bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[5]]]],{_,75.}],PlotMarkers->Automatic,Axes->False,Frame->True,FrameLabel->{"separation age","optimal retirement age"},ImageSize->720,BaseStyle->{FontSize->16,FontFamily->"Swiss"},PlotRange->{{34,75},{52,75}},GridLines->Automatic,Epilog->{Dashed,Gray,Opacity[0.6],Rectangle[{34,0},{39,100}],Opacity[1],Line[{{39,0},{39,100}}],Black,Text["refund\nzone",{35,70},{-1,0}]}]
],Style["Sample optimal retirement age for each separation age for a person with entry age of 34",{FontSize->16,FontFamily->"Swiss"}]]

Labeled[With[{entryAge=34},ListLinePlot[Thread[List[Range[34,75],bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-9]]]],PlotMarkers->Automatic,Axes->False,Frame->True,FrameLabel->{"separation age","present value\nat time of separation of pension benefit"},ImageSize->720,BaseStyle->{FontSize->16,FontFamily->"Swiss"},PlotRange->{{34,75},Automatic},GridLines->Automatic,Epilog->{Dashed,Gray,Opacity[0.6],Rectangle[{34,0},{39,100}],Opacity[1],Line[{{39,0},{39,100}}],Black,Text["refund\nzone",{35,70},{-1,0}]}]
],Style["Sample optimal present value at time of separation\nof pension benefit for a person with entry age of 34",{FontSize->16,FontFamily->"Swiss"}]]

Labeled[With[{entryAge=34},ListLinePlot[Thread[List[Range[34,75],bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1][[-9]]]],PlotMarkers->Automatic,Axes->False,Frame->True,FrameLabel->{"separation age","present value\nat time of separation of pension benefit"},ImageSize->720,BaseStyle->{FontSize->16,FontFamily->"Swiss"},PlotRange->{{34,75},Automatic},GridLines->Automatic,Epilog->{Dashed,Gray,Opacity[0.6],Rectangle[{34,0},{39,100}],Opacity[1],Line[{{39,0},{39,100}}],Black,Text["refund\nzone",{35,70},{-1,0}]}]
],Style["Sample optimal present value at time of entry\nof gross pension benefit for a person with entry age of 34",{FontSize->16,FontFamily->"Swiss"}]]

ListLinePlot[With[{entryAge=34},bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1]][[{-9,-7}]]]

Thread[Rule[{"αSalaryVector","αfasVector","αCumulativeInvestedContributions","αRefundRight","retirementAgeStar","ΩStar","SDAFStar","replacementRateStar","firstYearAnnuityPaymentsStar","PVAnnuityAsOfSeparationStar","annuityQ","pensionWealthStar","netPensionWealthStar","pensionWealthDeflatedStar","netPensionWealthDeflatedStar","discountedNetPensionWealthTrajectory","normalCostPercentage",
"normalCostTrajectory"},bothSides[salaryScale,fasYears,survival,survivalTableInitialAge,discountRate,inflation,txReplRate,True,True,0.,0.`,1000,killOffAge,{{0,1000000,0.064}},employerContributionRatesList,serviceDomain,retirementDomain,{entryAge,0},multipleHazardTX[[entryAge-19]],discountRate,1,1]]]

Part["retirementAgeStar"/.{"αSalaryVector"->{44130.`,45233.24999999999`,46364.081249999996`,47634.103015625`,48938.64831874999`,50278.64957264647`,51655.06423403855`,53191.310105145174`,54521.092857773794`,56270.01883693196`,58072.315432011936`,59929.558095072825`,62258.938340833854`,64667.331625073515`,67157.23273706214`,69731.21182238453`,72391.91659151236`,75142.0745917076`,77984.4955440429`,80922.07374737517`,83957.79055115899`,87094.71689903991`,90336.01594522041`,93684.94574564803`,97144.86202613128`,100719.22102954763`,104411.58244436848`,108225.61241678691`,112165.08664879852`,117498.57335428201`,121732.33445201156`,126104.34699628125`,131980.79924627545`,135280.3192274323`,140093.1962861561`,145062.16699829893`,151695.33482348375`,156879.00676789565`,165692.5580814627`,169834.8720334993`,174080.74383433675`,178432.76243019517`,182893.58149095002`,187465.9210282238`,192152.56905392933`,196956.3832802776`,201880.2928622845`,206927.30018384164`,212100.48268843765`,217402.99475564857`,222838.06962453978`},"αfasVector"->{0.`,0.`,0.`,0.`,0.`,46460.016516875`,47689.746431404295`,48974.10927821201`,50339.55504924104`,51716.95301767081`,53183.227121307194`,54741.96029318029`,56396.85906538714`,58210.38471252488`,60239.632465984825`,62417.07524601085`,64748.85452408537`,67241.32622337328`,69817.95347354804`,72481.3862573419`,75234.35445940452`,78079.6702051594`,81020.23026666493`,84059.01853736749`,87199.10857768852`,90443.66623343973`,93795.95232911746`,97259.32543818318`,100837.24473249646`,104533.27291312657`,108604.0151787567`,112806.6378632495`,117145.19077363206`,121896.22813952976`,126519.27465525652`,131038.19924163134`,135704.16575088882`,140822.3633163293`,145802.00482065335`,151884.4525914594`,157832.78774092806`,163636.50310813563`,168983.98862947794`,174186.9035740888`,178541.57616344103`,183005.115567527`,187580.2434567152`,192269.74954313305`,197076.49328171136`,202003.40561375415`,207053.490754098`},"αCumulativeInvestedContributions"->{0.`,2824.32`,5860.464`,9120.788400000001`,12625.410413000001`,16388.754426050004`,20426.02572000188`,24753.251116980442`,29395.157519558757`,34354.26533843422`,39673.259810919575`,45373.550989114316`,51477.72025665469`,58036.178323300795`,65076.696463470544`,72628.59418181605`,80722.82144753946`,89392.04518177324`,98670.7402147312`,108595.2849402865`,119204.06190713284`,130537.56359776366`,142638.5036591904`,155551.93386264404`,169325.36708349772`,184008.906607345`,199655.3820836033`,216320.49246422306`,234062.9562821086`,252944.66964173713`,273111.811818498`,294558.27181435167`,317356.8636128313`,341671.4779452345`,367412.99227305193`,394749.6064490185`,423771.0654593606`,454668.1201610316`,487441.7826022285`,522418.19544955355`,559408.5370321752`,598520.1314891815`,639865.8348591732`,683564.3158175526`,729740.3505542367`,778525.1325014`,830056.5976564077`,884479.7662824143`,941947.1018083009`,1.0026188877907761`*^6,1.0666636238446764`*^6},"αRefundRight"->{0.`,2824.32`,5860.464`,9120.788400000001`,12625.410413000001`,16388.754426050004`,20426.02572000188`,24753.251116980442`,29395.157519558757`,34354.26533843422`,39673.259810919575`,45373.550989114316`,51477.72025665469`,58036.178323300795`,65076.696463470544`,72628.59418181605`,80722.82144753946`,89392.04518177324`,98670.7402147312`,108595.2849402865`,119204.06190713284`,130537.56359776366`,142638.5036591904`,155551.93386264404`,169325.36708349772`,184008.906607345`,199655.3820836033`,216320.49246422306`,234062.9562821086`,252944.66964173713`,273111.811818498`,294558.27181435167`,317356.8636128313`,341671.4779452345`,367412.99227305193`,394749.6064490185`,423771.0654593606`,454668.1201610316`,487441.7826022285`,522418.19544955355`,559408.5370321752`,598520.1314891815`,639865.8348591732`,683564.3158175526`,729740.3505542367`,778525.1325014`,830056.5976564077`,884479.7662824143`,941947.1018083009`,1.0026188877907761`*^6,1.0666636238446764`*^6},"retirementAgeStar"->{75.`,75.`,75.`,75.`,75.`,63.`,63.`,63.`,63.`,63.`,63.`,63.`,63.`,63.`,63.`,63.`,64.`,63.`,62.`,61.`,60.`,59.`,58.`,57.`,56.`,55.`,55.`,55.`,55.`,55.`,55.`,56.`,57.`,58.`,59.`,60.`,61.`,62.`,63.`,64.`,65.`,66.`,67.`,68.`,69.`,70.`,71.`,72.`,73.`,74.`,75.`},"ΩStar"->{0.`,0.`,0.`,0.`,0.`,0.25257706950083364`,0.3183104513508693`,0.3900258592403288`,0.4681621164810094`,0.553185774940324`,0.6455951593092981`,0.7459219898547087`,0.854733420999082`,0.9726327573652843`,1.1002666023076288`,1.238324977386494`,1.4760174985787151`,1.7802154845122389`,2.136363507147968`,2.552149420684289`,3.036291175915966`,3.4186778432898106`,3.8254007597245394`,4.254040907721076`,4.701052411466824`,5.161502436750538`,5.645755197580363`,6.167028086691755`,6.728645466060077`,7.334079641444982`,7.987166403543981`,8.670967517025263`,9.361295383994428`,10.055428877159594`,10.75030256482559`,11.443382644526613`,11.554261106635877`,11.648330343866805`,11.72516785463156`,11.784264633493546`,11.825249322368455`,11.847848159696214`,11.851718208112334`,11.836419415571006`,11.801682795122561`,11.747685271279174`,11.675101604980414`,11.581066903729164`,11.469795810498464`,11.337114859866118`,11.186873609544723`},"SDAFStar"->{0.6754920347330223`,0.7093652382377885`,0.7449474771136763`,0.7823192397284768`,0.8215732260168718`,2.524508440787943`,2.6512614638586482`,2.7845067412031757`,2.9245509525300437`,3.0717184460010216`,3.2263626152388714`,3.3888600693049327`,3.559609449438123`,3.7390257077818174`,3.9275597997702176`,4.1256870810811055`,4.01091711570303`,4.55298077880368`,5.160298326444367`,5.84015885740112`,6.600632991121666`,7.450534691707118`,8.400089503128106`,9.460782625866955`,10.645499120169436`,11.968701302609945`,12.588082937748858`,13.241069429289867`,13.930942993913204`,14.660828868455736`,15.434137977862767`,15.201555955514134`,14.963707455234061`,14.720288211330105`,14.470726295363564`,14.215382167113807`,13.954421626371833`,13.687814740149008`,13.415523861134508`,13.137418766436506`,12.853531872139627`,12.563995927567566`,12.268859428687717`,11.9680681653903`,11.661741892413598`,11.350420551960555`,11.035067679565609`,10.713290382728182`,10.38930779936455`,10.059551783377211`,9.727716182212802`},"replacementRateStar"->{0.`,0.`,0.`,0.`,0.`,0.10004999999999999`,0.12005999999999999`,0.14007`,0.16008`,0.18009`,0.20009999999999997`,0.22011`,0.24011999999999997`,0.26013000000000003`,0.28014`,0.30015000000000003`,0.368`,0.391`,0.414`,0.437`,0.45999999999999996`,0.45885`,0.4554`,0.44965`,0.44160000000000005`,0.43124999999999997`,0.4485`,0.46575`,0.483`,0.50025`,0.5175`,0.5704`,0.6255999999999999`,0.6830999999999999`,0.7428999999999999`,0.8049999999999999`,0.828`,0.851`,0.874`,0.897`,0.9199999999999999`,0.943`,0.966`,0.989`,1.012`,1.035`,1.058`,1.081`,1.104`,1.127`,1.15`},"firstYearAnnuityPaymentsStar"->{0.`,0.`,0.`,0.`,0.`,4648.324652513344`,5725.630956554399`,6859.803486599157`,8058.355972282506`,9313.706068952335`,10641.963746973568`,12049.252880131913`,13542.01379878076`,15142.267375269099`,16875.530639020988`,18734.48513509016`,23827.578464863414`,26291.358553338956`,28904.632738048887`,31674.365794458412`,34607.803051326075`,35826.85667363739`,36896.612863439215`,37797.13768532729`,38507.12634790725`,39003.83106317088`,42067.48461960918`,45298.53082283382`,48704.389205795786`,52292.76977479156`,56202.57785500659`,64344.906237197516`,73286.0313479842`,83267.31344211278`,93991.16914139006`,105485.75038951322`,112363.04924173593`,119839.83118219624`,127430.95221325103`,136240.3539745391`,145206.1647216538`,154309.2224309719`,163238.5330160757`,172270.84763477382`,180684.07507740232`,189410.29461239045`,198459.89757720468`,207843.59925612682`,217572.44858300936`,227657.83812670092`,238111.51436721266`},"PVAnnuityAsOfSeparationStar"->{0.`,0.`,0.`,0.`,0.`,11734.734820792619`,15180.144711388808`,19101.1690517644`,23567.072634564967`,28609.08273263255`,34334.8339859629`,40833.23195043649`,48204.28028256145`,56617.32699023707`,66279.65573760944`,77292.62329254748`,95570.4422904776`,119704.05014198799`,149156.52794464273`,184983.32794706934`,228433.40657082395`,266929.238541754`,309934.8504151572`,357590.50352084555`,409927.57965689996`,466825.2036525516`,529548.9853741148`,599800.9916699696`,678498.0695793027`,766655.3487257736`,867438.341325746`,978142.6926170684`,1.0966307336463484`*^6,1.2257188524510616`*^6,1.3601204828262774`*^6,1.4995202549717047`*^6,1.567961364343963`*^6,1.6403454077126344`*^6,1.7095529800639604`*^6,1.7898465830510622`*^6,1.8664120662809338`*^6,1.9387404422088484`*^6,2.0027506149194315`*^6,2.0617492474025392`*^6,2.1070910476221466`*^6,2.14988650072138`*^6,2.1900184014441124`*^6,2.226688833022274`*^6,2.260427136990302`*^6,2.2901358115272545`*^6,2.3162812314811307`*^6},"annuityQ"->{0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`,1.`},"pensionWealthStar"->{0.`,2824.32`,5860.464`,9120.788400000001`,12625.410413000001`,16388.754426050004`,20426.02572000188`,24753.251116980442`,29395.157519558757`,34354.26533843422`,39673.259810919575`,45373.550989114316`,51477.72025665469`,58036.178323300795`,66279.65573760944`,77292.62329254748`,95570.4422904776`,119704.05014198799`,149156.52794464273`,184983.32794706934`,228433.40657082395`,266929.238541754`,309934.8504151572`,357590.50352084555`,409927.57965689996`,466825.2036525516`,529548.9853741148`,599800.9916699696`,678498.0695793027`,766655.3487257736`,867438.341325746`,978142.6926170684`,1.0966307336463484`*^6,1.2257188524510616`*^6,1.3601204828262774`*^6,1.4995202549717047`*^6,1.567961364343963`*^6,1.6403454077126344`*^6,1.7095529800639604`*^6,1.7898465830510622`*^6,1.8664120662809338`*^6,1.9387404422088484`*^6,2.0027506149194315`*^6,2.0617492474025392`*^6,2.1070910476221466`*^6,2.14988650072138`*^6,2.1900184014441124`*^6,2.226688833022274`*^6,2.260427136990302`*^6,2.2901358115272545`*^6,2.3162812314811307`*^6},"netPensionWealthStar"->{0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,1202.9592741388988`,4664.029110731426`,14847.620842938137`,30312.00496021475`,50485.787729911535`,76388.04300678284`,109229.34466369111`,136391.67494399037`,167296.3467559668`,202038.5696582015`,240602.21257340224`,282816.29704520653`,329893.6032905115`,383480.4992057466`,444435.1132971941`,513710.6790840365`,594326.529507248`,683584.4208027167`,779273.870033517`,884047.3745058271`,992707.4905532254`,1.1047706485226862`*^6,1.1441902988846025`*^6,1.1856772875516028`*^6,1.222111197461732`*^6,1.2674283876015088`*^6,1.3070035292487587`*^6,1.340220310719667`*^6,1.3628847800602582`*^6,1.3781849315849866`*^6,1.3773506970679099`*^6,1.37136136821998`*^6,1.3599618037877046`*^6,1.3422090667398598`*^6,1.3184800351820008`*^6,1.2875169237364784`*^6,1.2496176076364543`*^6},"pensionWealthDeflatedStar"->{0.`,2755.434146341464`,5578.074003569304`,8469.55873536368`,11437.998704518894`,14485.270868043746`,17613.297962386278`,20824.04962000546`,24125.974732688523`,27508.434604217517`,30992.687155539894`,34581.215134943304`,38276.56184555168`,42100.62628080904`,46907.91489111391`,53367.894181234755`,64378.632832627096`,78668.91006512278`,95634.08087378187`,115712.19870840777`,139406.27040843127`,158926.00788435695`,180030.2042952229`,202645.55069464957`,226638.8557932182`,251801.1217679299`,278667.0627933775`,307937.66610267945`,339844.60928014736`,374634.76279887574`,413544.88407256367`,454948.65733503026`,497618.732288436`,542629.3844785372`,587443.3037152573`,631854.4489013478`,644579.0752605053`,657888.4889331943`,668922.3079167776`,683258.5212303209`,695109.0099221187`,704435.4264195134`,709944.7096971879`,713033.0047386284`,710940.4498887865`,707687.6066662956`,703315.1230748587`,697650.4162721685`,690947.3788787432`,682954.5988344246`,673903.9758046741`},"netPensionWealthDeflatedStar"->{0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,851.3669936997604`,3220.351457054414`,10001.72760297894`,19920.899829873437`,32369.765998648385`,47782.8381046125`,66659.49515576292`,81205.73274747719`,97176.53708183386`,114494.69940288516`,133023.03349433758`,152548.44916907657`,173601.46842382042`,196878.7840655722`,222607.67451042772`,251030.50376648008`,283340.8255515729`,317945.24128913734`,353611.53341215535`,391370.4041662498`,428755.6692490116`,465518.3862773887`,470369.4501335229`,475536.08855921234`,478193.6870458691`,483829.8735089026`,486768.1395733486`,486964.9621597977`,483121.97850430827`,476629.90254104446`,464723.3091010175`,451417.06049345905`,436745.96650756267`,420531.4636910361`,403021.3181858757`,383957.84202709695`,363566.5059044848`},"discountedNetPensionWealthTrajectory"->{0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,0.`,607.5761781263084`,2243.477748255838`,6801.866182309624`,13225.027358985564`,20977.88757903358`,30229.342527832247`,41167.391224855266`,48956.750321736086`,57190.23457179`,65777.96094493486`,74603.02526201677`,83516.43639874703`,92779.51944480948`,102714.60708163114`,113372.56952507955`,124804.11958741915`,137513.75606587745`,150634.30402497613`,163543.21982089046`,176696.79387026708`,188966.6354457366`,200284.18611982933`,197552.93884041897`,194967.5952800903`,191389.16982651284`,189034.36658777695`,185654.20808101082`,181307.1509227288`,175593.55871231132`,169109.3566076673`,160959.0403437039`,152627.73274533558`,144151.42954367504`,135494.95089680504`,126761.45123971674`,117890.08800432684`,108971.31267620862`},"normalCostPercentage"->{0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`,0.13113842287662064`},"normalCostTrajectory"->{5787.138601545269`,5931.8170665839`,6080.112493248497`,6246.661144611541`,6417.737158234456`,6593.462809323135`,6773.96365724235`,6975.424517929992`,7149.810130878242`,7379.161525512993`,7615.511858547684`,7859.067732280664`,8164.538983989721`,8480.371880951554`,8806.893585896485`,9144.44114366307`,9493.361770826798`,9854.013153633923`,10226.763754474636`,10611.993127136366`,11010.092241084632`,11421.463815025854`,11846.52266001346`,12285.696032366053`,12739.42399667377`,13208.15979917664`,13692.370251806735`,14192.536127193847`,14709.152564942937`,15408.577599933464`,15963.786353126108`,16537.125182978438`,17307.753863152444`,17740.447709731252`,18371.600816711358`,19023.223799221887`,19893.08696649257`,20572.86552999253`,21728.660749195882`,22271.87726792578`,22828.674199623925`,23399.391054614523`,23984.375830979883`,24583.985226754383`,25198.584857423233`,25828.54947885882`,26474.263215830284`,27136.119796226045`,27814.522791131694`,28509.88586090998`,29222.63300743273`}},18]

S

NSPD=Expectation[valueOfPensionBenefits[separationTime],separationTime\[Distributed]separationDistribution]

Solve[NSPD==Expectation[TimeValue[Cashflow[Table[{t,augmentedNormalCostPercentage*salary[t]},{t,0,separationTime-1}]],r,0],
separationTime\[Distributed]separationDistribution],
augmentedNormalCostPercentage]

Solve[NSPD==Expectation[TimeValue[Cashflow[Table[{t,normalCostPercentage*salary[t]},{t,0,separationTime-1}]],r,0],
separationTime\[Distributed]separationDistribution]+Expectation[TimeValue[Cashflow[Table[{t,employeeContribution[salary[t]]*salary[t]},{t,0,separationTime-1}]],r,0],
separationTime\[Distributed]separationDistribution],
normalCostPercentage]