packages feed

currycarbon-0.2.1.2: src/Currycarbon/Calibration/MatrixMult.hs

{-# LANGUAGE Strict #-}

module Currycarbon.Calibration.MatrixMult
    (   calibrateDateMatrixMult
      , makeCalCurveMatrix
      , uncalToPDF
    ) where

import           Currycarbon.Calibration.Utils
import           Currycarbon.Parsers
import           Currycarbon.Types
import           Currycarbon.Utils

import qualified Data.Vector                   as V
import           Data.Vector.Generic           (convert)
import qualified Data.Vector.Unboxed           as VU

-- | Intercept calibration implemented with matrix multiplication (see 'MatrixMultiplication')
calibrateDateMatrixMult :: Bool -> Bool -> CalCurveBP -> UncalC14 -> Either CurrycarbonException CalPDF
calibrateDateMatrixMult allowOutside interpolate calCurve uncalC14 =
    if not allowOutside && isOutsideRangeOfCalCurve calCurve uncalC14
    then Left $ CurrycarbonCalibrationRangeException $ renderUncalC14 uncalC14
    else
        let rawCalCurveSegment = getRelevantCalCurveSegment uncalC14 calCurve
            calCurveSegment = prepareCalCurveSegment interpolate rawCalCurveSegment
            uncalPDF = uncalToPDF uncalC14
            calCurveMatrix = makeCalCurveMatrix uncalPDF calCurveSegment
            calPDF = projectUncalOverCalCurve uncalPDF calCurveMatrix
        in Right $ trimLowDensityEdgesCalPDF $ normalizeCalPDF calPDF

-- | Construct a matrix representation of a calibration curve for a given date
makeCalCurveMatrix :: UncalPDF -> CalCurveBCAD -> CalCurveMatrix
makeCalCurveMatrix (UncalPDF _ uncals' _) (CalCurveBCAD cals uncals sigmas) =
    let curveUnCalBCADsFloat = VU.map fromIntegral uncals
        sigmasFloat = VU.map fromIntegral sigmas
        uncalBCADs = vectorBPToBCAD uncals'
        uncalBCADsFloat = VU.map fromIntegral uncalBCADs
        matrix = buildMatrix curveUnCalBCADsFloat sigmasFloat uncalBCADsFloat
    in CalCurveMatrix uncalBCADs cals matrix
    where
        buildMatrix :: VU.Vector Float -> VU.Vector Float -> VU.Vector Float -> V.Vector (VU.Vector Float)
        buildMatrix curveuncal_ sigmas_ uncal_ =
          V.map (\x -> VU.map (fillCell x) uncal_) $
            V.zip (convert curveuncal_) (convert sigmas_)
        fillCell :: (Float, Float) -> Float -> Float
        fillCell (mean, sigma) matrixPosBP =
            if abs (mean - matrixPosBP) < 6*sigma
            then dnorm mean sigma matrixPosBP
            else 0

-- | Transform an uncalibrated date to an uncalibrated
-- probability density table
uncalToPDF :: UncalC14 -> UncalPDF
uncalToPDF (UncalC14 name mean std) =
    let meanFloat = fromIntegral mean
        stdFloat = fromIntegral std
        years = VU.reverse $ VU.fromList [(mean-5*std) .. (mean+5*std)]
        yearsFloat = VU.map fromIntegral years
        probabilities = VU.map (dnorm meanFloat stdFloat) yearsFloat
    in UncalPDF name years probabilities

projectUncalOverCalCurve :: UncalPDF -> CalCurveMatrix -> CalPDF
projectUncalOverCalCurve (UncalPDF name _ dens) (CalCurveMatrix _ cals matrix) =
    CalPDF name cals $ vectorMatrixMultSum dens matrix
    where
        vectorMatrixMultSum :: VU.Vector Float -> V.Vector (VU.Vector Float) -> VU.Vector Float
        vectorMatrixMultSum vec mat =
          convert $ V.map (\x -> VU.sum $ VU.zipWith (*) x vec) mat