emd 0.1.8.1 → 0.1.9.0
raw patch · 4 files changed
+104/−37 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Numeric.HHT: instance (Data.Vector.Generic.Base.Vector v a, GHC.TypeNats.KnownNat n, Data.Binary.Class.Binary (v a)) => Data.Binary.Class.Binary (Numeric.HHT.HHT v n a)
- Numeric.HHT: instance (Data.Vector.Generic.Base.Vector v a, GHC.TypeNats.KnownNat n, Data.Binary.Class.Binary (v a)) => Data.Binary.Class.Binary (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: instance Control.DeepSeq.NFData (v a) => Control.DeepSeq.NFData (Numeric.HHT.HHT v n a)
- Numeric.HHT: instance Control.DeepSeq.NFData (v a) => Control.DeepSeq.NFData (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: instance GHC.Classes.Eq (v a) => GHC.Classes.Eq (Numeric.HHT.HHT v n a)
- Numeric.HHT: instance GHC.Classes.Eq (v a) => GHC.Classes.Eq (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: instance GHC.Classes.Ord (v a) => GHC.Classes.Ord (Numeric.HHT.HHT v n a)
- Numeric.HHT: instance GHC.Classes.Ord (v a) => GHC.Classes.Ord (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: instance GHC.Show.Show (v a) => GHC.Show.Show (Numeric.HHT.HHT v n a)
- Numeric.HHT: instance GHC.Show.Show (v a) => GHC.Show.Show (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: newtype HHT v n a
+ Numeric.HHT: [hhtResidual] :: HHT v n a -> Vector v (n + 1) a
+ Numeric.HHT: [hlInitPhase] :: HHTLine v n a -> !a
+ Numeric.HHT: data HHT v n a
+ Numeric.HHT: ihht :: (Vector v a, Floating a) => HHT v n a -> Vector v (n + 1) a
+ Numeric.HHT: ihhtEmd :: (Vector v a, Floating a) => HHT v n a -> EMD v (n + 1) a
+ Numeric.HHT: instance (Control.DeepSeq.NFData (v a), Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Numeric.HHT.HHT v n a)
+ Numeric.HHT: instance (Control.DeepSeq.NFData (v a), Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Numeric.HHT.HHTLine v n a)
+ Numeric.HHT: instance (Data.Vector.Generic.Base.Vector v a, GHC.TypeNats.KnownNat n, Data.Binary.Class.Binary (v a), Data.Binary.Class.Binary a) => Data.Binary.Class.Binary (Numeric.HHT.HHT v n a)
+ Numeric.HHT: instance (Data.Vector.Generic.Base.Vector v a, GHC.TypeNats.KnownNat n, Data.Binary.Class.Binary (v a), Data.Binary.Class.Binary a) => Data.Binary.Class.Binary (Numeric.HHT.HHTLine v n a)
+ Numeric.HHT: instance (GHC.Classes.Eq a, GHC.Classes.Eq (v a)) => GHC.Classes.Eq (Numeric.HHT.HHT v n a)
+ Numeric.HHT: instance (GHC.Classes.Eq a, GHC.Classes.Eq (v a)) => GHC.Classes.Eq (Numeric.HHT.HHTLine v n a)
+ Numeric.HHT: instance (GHC.Classes.Ord a, GHC.Classes.Ord (v a)) => GHC.Classes.Ord (Numeric.HHT.HHT v n a)
+ Numeric.HHT: instance (GHC.Classes.Ord a, GHC.Classes.Ord (v a)) => GHC.Classes.Ord (Numeric.HHT.HHTLine v n a)
+ Numeric.HHT: instance (GHC.Show.Show a, GHC.Show.Show (v a)) => GHC.Show.Show (Numeric.HHT.HHT v n a)
+ Numeric.HHT: instance (GHC.Show.Show a, GHC.Show.Show (v a)) => GHC.Show.Show (Numeric.HHT.HHTLine v n a)
- Numeric.HHT: HHT :: [HHTLine v n a] -> HHT v n a
+ Numeric.HHT: HHT :: [HHTLine v n a] -> Vector v (n + 1) a -> HHT v n a
- Numeric.HHT: HHTLine :: !Vector v n a -> !Vector v n a -> HHTLine v n a
+ Numeric.HHT: HHTLine :: !Vector v (n + 1) a -> !Vector v n a -> !a -> HHTLine v n a
- Numeric.HHT: [hlMags] :: HHTLine v n a -> !Vector v n a
+ Numeric.HHT: [hlMags] :: HHTLine v n a -> !Vector v (n + 1) a
- Numeric.HHT: hilbertMagFreq :: forall v n a. (Vector v a, Vector v (Complex a), KnownNat n, FFTWReal a) => Vector v (n + 1) a -> (Vector v (n + 1) a, Vector v n a)
+ Numeric.HHT: hilbertMagFreq :: forall v n a. (Vector v a, Vector v (Complex a), KnownNat n, FFTWReal a) => Vector v (n + 1) a -> (Vector v (n + 1) a, (Vector v n a, a))
Files
- CHANGELOG.md +18/−0
- bench/bench.hs +15/−5
- emd.cabal +2/−2
- src/Numeric/HHT.hs +69/−30
CHANGELOG.md view
@@ -1,6 +1,24 @@ Changelog ========= +Version 0.1.9.0+---------------++*October 9, 2019*++<https://github.com/mstksg/emd/releases/tag/v0.1.9.0>++* *Breaking*: `hlMags` field in `HHT v n a` now contains `n + 1` items,+ instead of `n` items. Use `V.init` to regain the original behavior.+* Added `hlInitPhase` field to `HHTLine`, to denote the initial phase that an+ HHT Line starts at.+* Added `hhtResidual` field to `HHT`, to store the residual of the original+ EMD.+* Add `ihht` and `ihhtEmd`, to invert Hilbert-Huang Transform and reconstruct+ the original series (or the original EMD).+* Begin benchmarking `hht` and `ihht`.++ Version 0.1.8.0 ---------------
bench/bench.hs view
@@ -11,6 +11,7 @@ import Data.Complex import GHC.TypeNats import Numeric.EMD+import Numeric.HHT import Statistics.Transform import Text.Printf import qualified Data.Vector as UV@@ -27,30 +28,39 @@ test256 <- evaluate . force =<< generateData @8 g test1024 <- evaluate . force =<< generateData @10 g test4096 <- evaluate . force =<< generateData @12 g- test16384 <- evaluate . force =<< generateData @14 g itest256 <- evaluate . force $ emd defaultEO test256 itest1024 <- evaluate . force $ emd defaultEO test1024 itest4096 <- evaluate . force $ emd defaultEO test4096- itest16384 <- evaluate . force $ emd defaultEO test16384 + htest256 <- evaluate . force $ hhtEmd itest256+ htest1024 <- evaluate . force $ hhtEmd itest1024+ htest4096 <- evaluate . force $ hhtEmd itest4096+ let imfs256 = length . emdIMFs $ itest256 imfs1024 = length . emdIMFs $ itest1024 imfs4096 = length . emdIMFs $ itest4096- imfs16384 = length . emdIMFs $ itest16384 defaultMainWith defaultConfig [ bgroup "emd" [ bench (printf "256 (%d imfs)" imfs256 ) $ nf (emd defaultEO) test256 , bench (printf "1024 (%d imfs)" imfs1024 ) $ nf (emd defaultEO) test1024 , bench (printf "4096 (%d imfs)" imfs4096 ) $ nf (emd defaultEO) test4096- , bench (printf "16384 (%d imfs)" imfs16384) $ nf (emd defaultEO) test16384 ]+ , bgroup "hhtEmd"+ [ bench "256" $ nf hhtEmd itest256+ , bench "1024" $ nf hhtEmd itest1024+ , bench "4096" $ nf hhtEmd itest4096+ ] , bgroup "iemd" [ bench "256" $ nf iemd itest256 , bench "1024" $ nf iemd itest1024 , bench "4096" $ nf iemd itest4096- , bench "16384" $ nf iemd itest16384+ ]+ , bgroup "ihhtEmd"+ [ bench "256" $ nf ihhtEmd htest256+ , bench "1024" $ nf ihhtEmd htest1024+ , bench "4096" $ nf ihhtEmd htest4096 ] ]
emd.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: be01f23a1a945a33db65038fddfc7d9e9ea82364789729d8dc38eec68d5745e1+-- hash: 84c1c690a80a0ace06a7280dfac722ed522cdd34fb042042999e67d7a1217442 name: emd-version: 0.1.8.1+version: 0.1.9.0 synopsis: Empirical Mode Decomposition and Hilbert-Huang Transform description: Empirical Mode decomposition and Hilbert-Huang Transform in pure Haskell.
src/Numeric/HHT.hs view
@@ -34,6 +34,8 @@ HHT(..), HHTLine(..) , hhtEmd , hht+ , ihhtEmd+ , ihht -- ** Hilbert-Huang Spectrum , hhtSpectrum, hhtSparseSpectrum, hhtDenseSpectrum -- ** Properties of spectrum@@ -69,38 +71,55 @@ -- | A Hilbert Trasnform of a given IMF, given as a "skeleton line". data HHTLine v n a = HHTLine- { -- | IMF HHT Magnitude as a time series- hlMags :: !(SVG.Vector v n a)- -- | IMF HHT instantaneous frequency as a time series (between 0 and 1)- , hlFreqs :: !(SVG.Vector v n a)+ { -- | IMF HHT Magnitude as a time series.+ --+ -- It may be useful to "zip" this vector with 'hlFreqs'. To do this,+ -- use a function like 'SVG.init' or 'SVG.tail' to make these two+ -- vectors contain the same length, or 'weaken'/'shift' to make+ -- indices in 'hlFreqs' usable as indices in 'hlMags'.+ --+ -- Prior to v0.1.9.0, this was a length-n vector, just like+ -- 'hlFreqs'. To get the same behavior, use 'SVG.init' on this new+ -- field's value.+ hlMags :: !(SVG.Vector v (n + 1) a)+ -- | IMF HHT instantaneous frequency as a time series (between 0 and 1).+ --+ -- In reality, these frequencies are the frequencies "in between"+ -- each step in 'hlMags'.+ , hlFreqs :: !(SVG.Vector v n a)+ -- | Initial phase of skeleton line (between -pi and pi)+ --+ -- @since 0.1.9.0+ , hlInitPhase :: !a } deriving (Show, Eq, Ord, Generic) -- | @since 0.1.3.0-instance (VG.Vector v a, KnownNat n, Bi.Binary (v a)) => Bi.Binary (HHTLine v n a) where- put HHTLine{..} = Bi.put (SVG.fromSized hlMags )- *> Bi.put (SVG.fromSized hlFreqs)- get = do- Just hlMags <- SVG.toSized <$> Bi.get- Just hlFreqs <- SVG.toSized <$> Bi.get- pure HHTLine{..}+instance (VG.Vector v a, KnownNat n, Bi.Binary (v a), Bi.Binary a) => Bi.Binary (HHTLine v n a) -- | @since 0.1.5.0-instance NFData (v a) => NFData (HHTLine v n a)+instance (NFData (v a), NFData a) => NFData (HHTLine v n a) -- | A Hilbert-Huang Transform. An @'HHT' v n a@ is a Hilbert-Huang -- transform of an @n@-item time series of items of type @a@ represented -- using vector @v@. -- -- Create using 'hht' or 'hhtEmd'.-newtype HHT v n a = HHT { hhtLines :: [HHTLine v n a] }+data HHT v n a = HHT+ { -- | Skeleton lines corresponding to each IMF+ hhtLines :: [HHTLine v n a]+ -- | Residual from EMD+ --+ -- @since 0.1.9.0+ , hhtResidual :: SVG.Vector v (n + 1) a+ } deriving (Show, Eq, Ord, Generic) -- | @since 0.1.3.0-instance (VG.Vector v a, KnownNat n, Bi.Binary (v a)) => Bi.Binary (HHT v n a)+instance (VG.Vector v a, KnownNat n, Bi.Binary (v a), Bi.Binary a) => Bi.Binary (HHT v n a) -- | @since 0.1.5.0-instance NFData (v a) => NFData (HHT v n a)+instance (NFData (v a), NFData a) => NFData (HHT v n a) -- | Directly compute the Hilbert-Huang transform of a given time series. -- Essentially is a composition of 'hhtEmd' and 'emd'. See 'hhtEmd' for@@ -117,12 +136,35 @@ :: forall v n a. (VG.Vector v a, VG.Vector v (Complex a), KnownNat n, FFT.FFTWReal a) => EMD v (n + 1) a -> HHT v n a-hhtEmd EMD{..} = HHT $ map go emdIMFs+hhtEmd EMD{..} = HHT (map go emdIMFs) emdResidual where- go i = HHTLine (SVG.init m) f+ go i = HHTLine m f φ0 where- (m, f) = hilbertMagFreq i+ (m, (f, φ0)) = hilbertMagFreq i +-- | Invert a Hilbert-Huang transform back to an Empirical Mode+-- Decomposition+--+-- @since 0.1.9.0+ihhtEmd+ :: (VG.Vector v a, Floating a)+ => HHT v n a+ -> EMD v (n + 1) a+ihhtEmd HHT{..} = EMD (map go hhtLines) hhtResidual+ where+ go HHTLine{..} = SVG.zipWith (\m θ -> m * cos θ) hlMags θs+ where+ θs = SVG.scanl' (+) hlInitPhase ((* (2 * pi)) `SVG.map` hlFreqs)++-- | Construct a time series correpsonding to its hilbert-huang transform.+--+-- @since 0.1.9.0+ihht+ :: (VG.Vector v a, Floating a)+ => HHT v n a+ -> SVG.Vector v (n + 1) a+ihht = iemd . ihhtEmd+ -- | Fold and collapse a Hilbert-Huang transform along the frequency axis -- at each step in time along some monoid. --@@ -140,7 +182,7 @@ where split :: HHTLine v n a -> SV.Vector n b split HHTLine{..} = SVG.generate $ \i ->- f (hlFreqs `SVG.index` i) (hlMags `SVG.index` i)+ f (hlFreqs `SVG.index` i) (hlMags `SVG.index` weaken i) {-# INLINE split #-} pullBack :: SV.Vector n b -> SVG.Vector u n c pullBack v = SVG.generate $ \i -> g (v `SV.index` i)@@ -189,7 +231,7 @@ go :: HHTLine v n a -> [M.Map (Finite n, k) a] go HHTLine{..} = flip fmap (finites @n) $ \i -> M.singleton (i, f $ hlFreqs `SVG.index` i) $- hlMags `SVG.index` i+ hlMags `SVG.index` weaken i -- | A denser version of 'hhtSpectrum'. Compute the full Hilbert-Huang -- Transform spectrum, returning a dense matrix (as a vector of vectors)@@ -226,7 +268,7 @@ where go :: HHTLine v n a -> [M.Map k a] go HHTLine{..} = flip fmap (finites @n) $ \i ->- M.singleton (f $ hlFreqs `SVG.index` i) (hlMags `SVG.index` i)+ M.singleton (f $ hlFreqs `SVG.index` i) (hlMags `SVG.index` weaken i) -- | Compute the mean marginal spectrum given a Hilbert-Huang Transform. It -- is similar to a Fourier Transform; it provides the "total power" over@@ -303,7 +345,7 @@ in M.singleton fr $ if mm == 0 then 0- else (1 - (hlMags `SVG.index` i / mm)) ^ (2 :: Int)+ else (1 - (hlMags `SVG.index` weaken i / mm)) ^ (2 :: Int) -- | Given a time series, return a time series of the /magnitude/ of the -- hilbert transform and the /frequency/ of the hilbert transform, in units@@ -316,12 +358,13 @@ hilbertMagFreq :: forall v n a. (VG.Vector v a, VG.Vector v (Complex a), KnownNat n, FFT.FFTWReal a) => SVG.Vector v (n + 1) a- -> (SVG.Vector v (n + 1) a, SVG.Vector v n a)-hilbertMagFreq v = (hilbertMag, hilbertFreq)+ -> (SVG.Vector v (n + 1) a, (SVG.Vector v n a, a))+hilbertMagFreq v = (hilbertMag, (hilbertFreq, φ0)) where v' = hilbert v hilbertMag = SVG.map magnitude v' hilbertPhase = SVG.map phase v'+ φ0 = SVG.head hilbertPhase hilbertFreq = SVG.map ((`mod'` 1) . (/ (2 * pi))) $ SVG.tail hilbertPhase - SVG.init hilbertPhase -- | The polar form of 'hilbert': returns the magnitude and phase of the@@ -336,9 +379,6 @@ -- enforces the phase to be monotonically increasing at the slowest -- possible detectable rate. ----- Note that this function effectively resets the initial phase to be zero,--- conceptually rotating 'hilbert' to begin on the real axis.--- -- @since 0.1.6.0 hilbertPolar :: forall v n a. (VG.Vector v a, VG.Vector v (Complex a), KnownNat n, FFT.FFTWReal a)@@ -348,10 +388,9 @@ where hilbertMag :: SVG.Vector v (n + 1) a hilbertFreq :: SVG.Vector v n a- (hilbertMag, hilbertFreq) = hilbertMagFreq v+ (hilbertMag, (hilbertFreq, φ0)) = hilbertMagFreq v hilbertPhase :: SVG.Vector v (n + 1) a- hilbertPhase = SVG.scanl' (+) 0 hilbertFreq-+ hilbertPhase = SVG.scanl' (+) φ0 ((* (2 * pi)) `SVG.map` hilbertFreq) -- | Real part is original series and imaginary part is hilbert transformed -- series. Creates a "helical" form of the original series that rotates