type-checking polymorphic units for astrophysics research in haskell
TRANSCRIPT
-
ExperienceReport:TypecheckingPolymorphicUnitsforAstrophysics
ResearchinHaskell
TakayukiMuranushiAdvancedInstituteforComputationalScience,
RichardA.EisenbergUniversityofPennsylvania
-
July23,
AirCanadaFlight143,aBoeing767233wasdepartingMontrealtoEdmonton.
Thefuelgaugeisnotworking. Thecrewsuseabackupsystemandmanuallycalculatedtheamountoffueltoberefueled.
1983 .
-
Manualcalculationmassoffuelrequired densityoffuel volumetoberefueled
[kg] [pound/L] [*L]
[kg] [kg/L] [L]
massoffuelrequired densityoffuel volumetoberefueled
Thecorrectcalculation
Flight143tookoffwith22,300pounds offueltoEdmonton,where22,300kg wasactuallyneeded.
-
Flight143madeanemergencylandingonrunway32LofGimliabandonedairport.
ItwasFamilyDayfestival;gocarts,campers,familiesandbarbecueswereon32L.
Noonewasseriouslyhurtnorkilled.
WadeH.Nelson(1997)
-
[kg] [kg/L] [L]
Mass Density Volume
[lb.] [lb./L] [L]
Asamelawofphysicscanberepresentedinmanydifferentunits
DimensionsLevel:
UnitsLevel:
Massoffuelrequired Densityoffuel
Volumetoberefueled
QuantityLevel:
quantityvalue =numericalvalue[unit ]
[kg] [lb./L] [L]
-
ToavoidmistakeslikeGimliGlider,wewouldliketousetypesystemtoenforcethecorrectnessofthedimensionsandunitsinourcalculations.
Suchcorrectnessoflawsofphysicsismorethanjustaboutspecificsetofunits;wecanrepresentonequantityinmanydifferentunits,buttheymeanthesamequantity.
unitsofmeasure aretosciencewhattypesaretoprogramming A.J.Kennedy
-
unitsofmeasure aretosciencewhattypesaretoprogramming A.J.Kennedy
Lawsofphysicsaredimensionmonomorphicandunitpolymorphic
T.Muranushi
[kg] [kg/L] [L] [lb.] [lb./L] [L]
Mass Density Volume DimensionsLevel:
UnitsLevel:
-
WealreadyhavetypesystemofunitsformanylanguagesincludingC,F#,simulink andofcourseinHaskell;wealreadyhavepolymorphism.Willtheyblend?
unitsofmeasurearetosciencewhattypesaretoprogramming A.J.Kennedy
Lawsofphysicsaredimensionmonomorphicandunitpolymorphic
T.Muranushi
-
Using`unittyped` byThijsAlkemade,IstartedanattempttoencodeknowledgeofphysicsinHaskell.
-
Aunitmonomorphicquantityfunctionrefuel::Fractionalf=>
ValueMassKiloGram f>ValueDensityKiloGramPerLiter f>ValueVolumeLiterf
refuelgasMassgasDen=gasMass|/|gasDen
refuel::Fractionalf=>ValueMassKiloGram f
>ValueDensityKiloGramPerLiter f>ValueVolumeLiterf
refuelgasMassgasDen=gasMass|/|gasDen
Aunitpolymorphicversion?
Thiscodedoesntcompile.
refuel::Fractionalf=>ValueMassuniMass f Here we replace the unit types
>ValueDensityuniDen f with type variables>ValueVolumeuniVol f
refuelgasMass gasDen =gasMass |/|gasDen
-
refuel::(Fractionalf,Convertible'MassuniMass,Convertible'DensityuniDen,Convertible'Volume uniVol,MapNeg negUniDen uniDen, negUniDen = 1 / uniDenMapMerge uniMass negUniDen uniVol uniMass * negUniDen = uniVol
)=>ValueMassuniMass f
>ValueDensityuniDen f>ValueVolumeuniVol f
refuelgasMassgasDen=gasMass|/|gasDen
refuel::(Fractionalf,Convertible'MassuniMass,Convertible'DensityuniDen,Convertible'Volume uniVol,MapNeg negUniDen uniDen, negUniDen = 1 / uniDenMapMerge uniMass negUniDen uniVol uniMass * negUniDen = uniVol
)=>ValueMassuniMass f
>ValueDensityuniDen f>ValueVolumeuniVol f
refuelgasMassgasDen=gasMass|/|gasDen
needstheseannotationstocompile:
refuel::Fractionalf=>ValueMassuniMass f
>ValueDensityuniDen f>ValueVolumeuniVol f
refuelgasMass gasDen =gasMass |/|gasDen
Thiscode
-
refuel::(Fractionalf,Convertible'Massumass,Convertible'Densityuden,Convertible'Volume uvol,MapNeg negUden uden,MapMerge umass negUden uvol)=>ValueMassumass f
>ValueDensityuden f>ValueVolumeuvol frefuelgasMassgasDen=gasMass|/|gasDen
Problemwithunitpolymorphismin`unittyped`toomuchtypeconstraint!!
gravityPoisson::(Fractionalx,dimLen~LengthDimension,dimPot~'['(Time,NTwo),'(Length,PTwo)],dimDen~Density,dimZhz ~'['(Time,NTwo)],Convertible'dimLenuniLen,Convertible'dimPotuniPot,Convertible'dimDenuniDen,Convertible'dimZhz uniZhz,Convertible'dimZhz uniZhz',MapMergedimLendimLen dimLen2,MapNegdimLen2dimLenNeg2,MapMergedimPotdimLenNeg2dimZhz,MapMergedimDen'['(Time,NTwo),'(Length,PThree),
'(Mass,NOne)]dimZhz,MapMergeuniLen uniLen uniLen2,MapNeguniLen2uniLenNeg2,MapMergeuniPot uniLenNeg2uniZhz,MapMergeuniDen '['(Second,NTwo),'(Meter,
PThree),'((KiloGram),NOne)]uniZhz')=>
(forall s.AD.Mode s=>Vec3(ValuedimLenuniLen (ADsx))>ValuedimPot
uniPot (ADsx))>(Vec3(ValuedimLenuniLen x)>(ValuedimDen
uniDen x))>(Vec3(ValuedimLenuniLen x)>(ValuedimZhz
uniZhz x))
gravityPoissongravitationalPotential densityr=laplacian gravitationalPotential r||(4*|pi|*|
densityr|*|g)
gravityPoisson ::(Fractional x, dimLen ~ LengthDimension, dimPot ~ '[ '(Time, NTwo), '(Length, PTwo)], dimDen ~ Density, dimZhz ~ '[ '(Time, NTwo)], Convertible' dimLen uniLen, Convertible' dimPot uniPot, Convertible' dimDen uniDen, Convertible' dimZhz uniZhz, Convertible' dimZhz uniZhz', MapMerge dimLen dimLen dimLen2, MapNeg dimLen2 dimLenNeg2, MapMerge dimPot dimLenNeg2 dimZhz, MapMerge dimDen '[ '(Time, NTwo), '(Length, PThree), '(Mass, NOne) ] dimZhz, MapMerge uniLen uniLen uniLen2, MapNeg uniLen2 uniLenNeg2, MapMerge uniPot uniLenNeg2 uniZhz, MapMerge uniDen '[ '(Second, NTwo), '(Meter, PThree), '((Kilo Gram), NOne) ] uniZhz') =>(forall s. AD.Mode s =>Vec3 (Value dimLen uniLen (AD s x)) > Value dimPot uniPot (AD s x))> (Vec3 (Value dimLen uniLen x) > (Value dimDen uniDen x))> (Vec3 (Value dimLen uniLen x) > (Value dimZhz uniZhz x))gravityPoisson gravitationalPotential density r= laplacian gravitationalPotential r || (4 *| pi |*| density r |*| g)gravitationalPotentialToDensity ::forall xdimLen dimDen dimDen' dimLen2 dimNegLen2 dimZhz dimNegGC dimPotuniLen uniDen uniDen' uniLen2 uniNegLen2 uniZhz uniNegGC uniPot .(Fractional x, dimLen ~ LengthDimension, dimPot ~ '[ '(Time, NTwo), '(Length, PTwo)], dimDen ~ Density, MapEq dimDen' dimDen, Convertible' dimLen uniLen, Convertible' dimPot uniPot, Convertible' dimZhz uniZhz, Convertible' dimDen' uniDen', Convertible' dimDen uniDen, MapMerge dimLen dimLen dimLen2, MapNeg dimLen2 dimNegLen2, MapMerge dimPot dimNegLen2 dimZhz, MapNeg '[ '(Time, NTwo), '(Length, PThree), '(Mass, NOne) ] dimNegGC, dimNegGC ~ '[ '(Time, PTwo), '(Length, NThree), '(Mass, POne) ], MapMerge dimZhz dimNegGC dimDen', MapMerge uniLen uniLen uniLen2, MapNeg uniLen2 uniNegLen2, MapMerge uniPot uniNegLen2 uniZhz, MapNeg '[ '(Second, NTwo), '(Meter, PThree), '((Kilo Gram), NOne) ] uniNegGC, uniNegGC ~ '[ '(Second, PTwo), '(Meter, NThree), '((Kilo Gram), POne) ], MapMerge uniZhz uniNegGC uniDen') =>(forall s. AD.Mode s =>Vec3 (Value dimLen uniLen (AD s x)) > Value dimPot uniPot (AD s x))> (Vec3 (Value dimLen uniLen x) > (Value dimDen uniDen x))gravitationalPotentialToDensity gravitationalPotential r= to (undefined :: Value dimDen uniDen x) $(laplacian gravitationalPotential r |/| (4 *| pi |*| g) :: Value dimDen' uniDen' x)
hydrostatic ::forall xdimLen dimPre dimDen dimAcc dimGpr dimNegLen dimNegDen dimAcc'uniLen uniPre uniDen uniAcc uniGpr uniNegLen uniNegDen uniAcc' .( Fractional x, dimLen ~ LengthDimension, dimPre ~ Pressure, dimDen ~ Density, dimAcc ~ Acceleration, dimNegDen ~ '[ '(Length, PThree), '(Mass, NOne) ], dimGpr ~ '[ '(Length, NTwo), '(Mass, POne) , '(Time, NTwo) ], Convertible' dimLen uniLen, Convertible' dimPre uniPre, Convertible' dimGpr uniGpr, Convertible' dimDen uniDen, Convertible' dimAcc uniAcc, Convertible' dimAcc' uniAcc', MapNeg dimLen dimNegLen, MapMerge dimPre dimNegLen dimGpr, MapNeg dimDen dimNegDen, MapMerge dimGpr dimNegDen dimAcc', MapEq dimAcc' dimAcc, MapNeg uniLen uniNegLen, MapMerge uniPre uniNegLen uniGpr, MapNeg uniDen uniNegDen, MapMerge uniGpr uniNegDen uniAcc') =>(forall s. AD.Mode s =>Vec3 (Value dimLen uniLen (AD s x)) > Value dimPre uniPre (AD s x))> (Vec3 (Value dimLen uniLen x) > Value dimDen uniDen x)> (Vec3 (Value dimLen uniLen x) > Vec3 (Value dimAcc uniAcc x))> (Vec3 (Value dimLen uniLen x) > Vec3 (Value dimAcc uniAcc x))hydrostatic pressure density externalAcc r= compose $ i > to (undefined :: Value dimAcc uniAcc x) $(externalAcc r ! i) |+| (gradP r ! i) |/| (density r)wheregradP :: Vec3 (Value dimLen uniLen x) > Vec3 (Value dimGpr uniGpr x)gradP = grad pressure
pressureToAcc ::forall xdimLen dimPre dimDen dimAcc dimGpr dimNegLen dimNegDen dimAcc'uniLen uniPre uniDen uniGpr uniNegLen uniNegDen uniAcc' .( Fractional x, dimLen ~ LengthDimension, dimPre ~ Pressure, dimDen ~ Density, dimAcc ~ Acceleration, dimNegDen ~ '[ '(Length, PThree), '(Mass, NOne) ], dimGpr ~ '[ '(Length, NTwo), '(Mass, POne) , '(Time, NTwo) ], Convertible' dimLen uniLen, Convertible' dimPre uniPre, Convertible' dimGpr uniGpr, Convertible' dimDen uniDen, Convertible' dimAcc' uniAcc', Convertible' dimAcc uniAcc', MapNeg dimLen dimNegLen, MapMerge dimPre dimNegLen dimGpr, MapNeg dimDen dimNegDen, MapMerge dimGpr dimNegDen dimAcc', MapEq dimAcc' dimAcc, MapNeg uniLen uniNegLen, MapMerge uniPre uniNegLen uniGpr, MapNeg uniDen uniNegDen, MapMerge uniGpr uniNegDen uniAcc') =>(forall s. AD.Mode s =>Vec3 (Value dimLen uniLen (AD s x)) > Value dimPre uniPre (AD s x))> (Vec3 (Value dimLen uniLen x) > Value dimDen uniDen x)> (Vec3 (Value dimLen uniLen x) > Vec3 (Value dimAcc uniAcc' x))pressureToAcc pressure density r= compose $ i > to (undefined :: Value dimAcc uniAcc' x) $(gradP r ! i) |/| (density r)wheregradP :: Vec3 (Value dimLen uniLen x) > Vec3 (Value dimGpr uniGpr x)gradP = grad pressure
gravitationalPotentialToAcc ::forall xdimLen dimPot dimAcc' dimNegLen dimAccuniLen uniPot uniAcc' uniNegLen( Fractional x, dimLen ~ LengthDimension, dimPot ~ '[ '(Time, NTwo), '(Length, PTwo)], dimAcc' ~ '[ '(Length, POne), '(Time, NTwo)], dimAcc ~ Acceleration, Convertible' dimLen uniLen, Convertible' dimPot uniPot, Convertible' dimAcc' uniAcc', Convertible' dimAcc uniAcc', MapNeg dimLen dimNegLen, MapMerge dimPot dimNegLen dimAcc', MapEq dimAcc' dimAcc, MapNeg uniLen uniNegLen, MapMerge uniPot uniNegLen uniAcc') =>(forall s. AD.Mode s =>Vec3 (Value dimLen uniLen (AD s x)) > Value dimPot uniPot (AD s x))> (Vec3 (Value dimLen uniLen x) > Vec3 (Value dimAcc uniAcc' x))gravitationalPotentialToAcc pot r =(fmap $ to (undefined :: Value dimAcc uniAcc x)) $grad pot r
Weneedatleasttwotypeconstraintsperonearithmeticoperator,inordertoencodetypelevel unitcalculations.
Colorsindicate: Typeconstraints, Types,Values
-
Problem Solution
-
Oursolution:
-
Quantityrepresentationin`unittyped`
>:t88*|mile|/|hour ::Fractionalf=>
Value'['(Length,POne),'(Time,NOne)]'['(Mile,POne),'(Hour,NOne)]f
>:t88*|mile|/|hour ::Fractionalf=>
Value'['(Length,POne),'(Time,NOne)]'['(Mile,POne),'(Hour,NOne)]f
Typeconstructortakes(dimensions)(units)(numericalvalue)
>:t88 % mile:/hour::Fractionalf=>QuVelocitySIf ::Fractionalf=>
Qu'['(Length,One),'(Time,MOne)]'['(Length,Meter),'(Time,Second),]f
>:t88 % mile:/hour::Fractionalf=>QuVelocitySIf ::Fractionalf=>
Qu'['(Length,One),'(Time,MOne)]'['(Length,Meter),'(Time,Second),]f
Typeconstructortakes(dimensions)(mapfromdimensionstounits) (numericalvalue)
Quantityrepresentationin`units`
-
SystemofUnitsastypeargument Themapfromdimensionstounitsrepresentsasystemofunits;e.g.SIsystem,CGS(centimetergramsecond)system,etc.>88%Miles:/Hour::QuVelocitySIFloat39.33952m/s>:infoSItypeSI=MkLCSU
'[(Length,Meter),(Mass,Kilo:@Gram),(Time,Second),(Current,Ampere),(Temperature,Kelvin),(AmountOfSubstance,Mole),(LuminousIntensity,Lumen)]
>:infoCGStypeCGS=MkLCSU
'[(Length,Centi:@Meter),(Mass,Gram),(Time,Second)]
>88%Miles:/Hour::QuVelocitySIFloat39.33952m/s>:infoSItypeSI=MkLCSU
'[(Length,Meter),(Mass,Kilo:@Gram),(Time,Second),(Current,Ampere),(Temperature,Kelvin),(AmountOfSubstance,Mole),(LuminousIntensity,Lumen)]
>:infoCGStypeCGS=MkLCSU
'[(Length,Centi:@Meter),(Mass,Gram),(Time,Second)]
-
[Def]Acoherentsystemofunit
AJoule(1 [kg/m2s2])isthecoherentderivedunitofenergyinSI
Anerg(1 [g/cm2s2] = 10-7J)isthecoherentderivedunitofenergyincentimetergramsecondsystem
[kg] [kg/m3] [m3]
[SI mass] [SI density] [SI volume]
= {u1, u2, , un} {u1p u2q unr | p,q, , r }baseunits unitsderivedbyproductsofbaseunits
1:1mappingbetweendimensionsandunits
-
localcoherentsystemofunit istheonlyone,unconstrained,typevariable
Thecomputationisnondimensionalized;canbecarriedoutwithoutdetailsunits.
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
Unitpolymorphiccalculationsin`units`
[kg] [kg/m3] [m3]
[SI mass] [SI density] [SI volume]
-
Unitpolymorphismwithfundeps gaverisetooverwhelmingcomplexesofconstrainedtypevariables
Takelocalcoherentsystemofunit asonlyonefreevariable
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
refuel::(Fractionalf,Convertible'Massumass,Convertible'Densityuden,Convertible'Volume uvol,MapNeg negUden uden,MapMerge umass negUden uvol)=>ValueMassumass f
>ValueDensityuden f>ValueVolumeuvol frefuelgasMassgasDen=gasMass|/|gasDen
refuel::(Fractionalf,Convertible'Massumass,Convertible'Densityuden,Convertible'Volume uvol,MapNeg negUden uden,MapMerge umass negUden uvol)=>ValueMassumass f
>ValueDensityuden f>ValueVolumeuvol frefuelgasMassgasDen=gasMass|/|gasDen
-
Anapplication:Avoidover/underflow
-
ljForce::Energy Float>Length Float>Length Float>Force Float
ljForceepssigmar=(24*|eps|*|sigma|pSix) |/|(r|pSeven)||(48*|eps|*|sigma|pTwelve)|/|(r|pThirteen)
ljForce::Energy Float>Length Float>Length Float>Force Float
ljForceepssigmar=(24*|eps|*|sigma|pSix) |/|(r|pSeven)||(48*|eps|*|sigma|pTwelve)|/|(r|pThirteen)
Solution #1:
>letsigmaAr=3.4e8%MeterepsAr=1.68e21%Jouler=4.0e8%Meter
>(ljForceepsArsigmaArr::ForceSIFloat)#NewtonNaN
>letsigmaAr=3.4e8%MeterepsAr=1.68e21%Jouler=4.0e8%Meter
>(ljForceepsArsigmaArr::ForceSIFloat)#NewtonNaN
Exercise: How many Newtons is the Lennard-Jones force F between two argon atoms at distance 4, where
-
ljForce::Energy Float>Length Float>Length Float>Force Float
ljForceepssigmar=(24*|eps|*|sigma|pSix) |/|(r|pSeven)||(48*|eps|*|sigma|pTwelve)|/|(r|pThirteen)
typeCU=MkLCSU'['(Length,Angstrom),'(Mass,ProtonMass),'(Time,Pico:@Second)]
ljForce::Energy Float>Length Float>Length Float>Force Float
ljForceepssigmar=(24*|eps|*|sigma|pSix) |/|(r|pSeven)||(48*|eps|*|sigma|pTwelve)|/|(r|pThirteen)
typeCU=MkLCSU'['(Length,Angstrom),'(Mass,ProtonMass),'(Time,Pico:@Second)]
Solution #2:
>(ljForceepsArsigmaArr::ForceCUFloat)#Newton9.3407324e14>(ljForceepsArsigmaArr::ForceCUFloat)#Newton9.3407324e14
Exercise: How many Newtons is the Lennard-Jones force F between two argon atoms at distance 4, where
-
AstrophysicsresearchinHaskell
A27pageastrophysicspaperhasbeenwritteninHaskell;itsquantitativereasoningispoweredbytheunits library.
-
Experience Conclusion
-
Whenyoudesigntypesystemofunitsconsiderunitpolymorphism
because,withunitpolymorphism Wecanfaithfullyexpressunitindependentnatureoflawsofphysics.
Wecanwritequantityexpressions,whichuserscanlaterinterpretinunitsystemsoftheirchoice.
Wecanavoidoverflows/underflowsbyappropriatelychoosingsystemofunits.
-
Aneasywaytoimplementunitpolymorphismistotakelocalcoherentsystemofunit asonlyonefreevariable
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
-
Inthepaper
Extensibility Quantitycombinators Valuelevelunits Protectnumericalvaluesfrommanipulation
Thingscameafterpaper defaultLCSU TemplateHaskell
-
Comments Haskellissuchacoollanguagethatallowssomethinglike`units` atall.Itstypesystemissoprogrammablethatthesefeaturescanbebuiltontopof,insteadofbeingintegratedin(likeF#)orexternallyanalyzed(likeC)
Asfarasweknow,`units` istheonlypracticalsystemthatsupportsunitpolymorphism.
HeartfeltthankstoallpeoplesworkthatenabledGHC7.8,andtoRichardwhoimplemented`units`.
-
Thanks! Questions?cabalinstallunits
andenjoyunitpolymorphism!
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen
refuel::Fractionalf=>QuMass f>QuDensity f>QuVolume frefuelgasMassgasDen=gasMass|/|gasDen