diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -5,6 +5,12 @@
 ## Unreleased changes
 
 
+## Version 0.9.0.0
+
+-   Toolchain update
+-   Update to random version 1.3
+
+
 ## Version 0.8.0.0
 
 -   Adapt to breaking changes in upstream libraries (`data-default`).
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 # The ELynx Suite
 
-Version: 0.8.0.0.
+Version: 0.9.0.0.
 Reproducible evolution made easy.
 
 <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>
@@ -73,9 +73,9 @@
     # OR: stack exec slynx -- --help
     # OR: slynx --help
 
-    ELynx Suite version 0.8.0.0.
+    ELynx Suite version 0.9.0.0.
     Developed by Dominik Schrempf.
-    Compiled on October 27, 2024, at 07:14 am, UTC.
+    Compiled on August 11, 2025, at 07:19 am, UTC.
     
     Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]
                  [-f|--force] [--no-elynx-file] COMMAND
@@ -143,9 +143,9 @@
     # OR: stack exec slynx -- simulate --help
     # OR: slynx simulate --help
 
-    ELynx Suite version 0.8.0.0.
+    ELynx Suite version 0.9.0.0.
     Developed by Dominik Schrempf.
-    Compiled on October 27, 2024, at 07:14 am, UTC.
+    Compiled on August 11, 2025, at 07:19 am, UTC.
     
     Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL]
                           [-m|--mixture-model MODEL] [-n|--global-normalization]
diff --git a/elynx-markov.cabal b/elynx-markov.cabal
--- a/elynx-markov.cabal
+++ b/elynx-markov.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               elynx-markov
-version:            0.8.0.0
+version:            0.9.0.0
 synopsis:           Simulate molecular sequences along trees
 description:
   Examine, modify, and simulate molecular sequences in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>.
@@ -99,5 +99,5 @@
   -- ghc-options:
   hs-source-dirs:   bench
   ghc-options:      -Wall -Wunused-packages
-  build-depends:    base
+  build-depends:    base <5
   default-language: Haskell2010
diff --git a/src/ELynx/MarkovProcess/AminoAcid.hs b/src/ELynx/MarkovProcess/AminoAcid.hs
--- a/src/ELynx/MarkovProcess/AminoAcid.hs
+++ b/src/ELynx/MarkovProcess/AminoAcid.hs
@@ -127,8 +127,7 @@
 pamlToAlphaMat m =
   build
     (n, n)
-    ( \i j -> m ! alphaIndexToPamlIndex (round i) ! alphaIndexToPamlIndex (round j)
-    )
+    (\i j -> m ! alphaIndexToPamlIndex (round i) ! alphaIndexToPamlIndex (round j))
 
 -- Lower triangular matrix of LG exchangeabilities in PAML order and in form of
 -- a list.
diff --git a/src/ELynx/MarkovProcess/GammaRateHeterogeneity.hs b/src/ELynx/MarkovProcess/GammaRateHeterogeneity.hs
--- a/src/ELynx/MarkovProcess/GammaRateHeterogeneity.hs
+++ b/src/ELynx/MarkovProcess/GammaRateHeterogeneity.hs
@@ -101,7 +101,7 @@
     means =
       V.fromList
         [ integralAToB meanFunc (quantiles !! i) (quantiles !! (i + 1))
-          | i <- [0 .. n - 2]
+        | i <- [0 .. n - 2]
         ]
     -- The last category has to be calculated with an improper integration.
     lastMean = integralAToInf meanFunc (quantiles !! (n - 1))
diff --git a/src/ELynx/Simulate/MarkovProcessAlongTree.hs b/src/ELynx/Simulate/MarkovProcessAlongTree.hs
--- a/src/ELynx/Simulate/MarkovProcessAlongTree.hs
+++ b/src/ELynx/Simulate/MarkovProcessAlongTree.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 -- |
@@ -30,6 +31,7 @@
 import Control.Concurrent
 import Control.Concurrent.Async
 import Control.Monad
+import Control.Monad.IO.Class (MonadIO (liftIO))
 import Data.Tree
 import qualified Data.Vector as V
 import ELynx.MarkovProcess.RateMatrix
@@ -97,7 +99,7 @@
 
 -- | See 'simulateAndFlatten', parallel version.
 simulateAndFlattenPar ::
-  (RandomGen g) =>
+  (SplitGen g) =>
   Int ->
   StationaryDistribution ->
   ExchangeabilityMatrix ->
@@ -216,17 +218,25 @@
 -- requires benchmarks. I am just not sure if it makes sense to spend more time
 -- on this since the parallelization itself is a bit weird. Like so, we walk
 -- along separate trees in each process.
-parComp :: (RandomGen g) => Int -> (Int -> IOGenM g -> IO b) -> IOGenM g -> IO [b]
+parComp ::
+  (FrozenGen g IO, SplitGen g, ThawedGen g IO) =>
+  Int -> (Int -> MutableGen g IO -> IO b) -> MutableGen g IO -> IO [b]
 parComp num fun gen = do
-  ncap <- getNumCapabilities
+  ncap <- liftIO getNumCapabilities
+  genF <- freezeGen gen
   let chunks = getChunks ncap num
-  rs <- replicateM ncap $ splitGenM gen
-  gs <- mapM newIOGenM rs
+      rs = splitGenN ncap genF
+  gs <- mapM thawGen rs
   mapConcurrently (uncurry fun) (zip chunks gs)
+  where
+    splitGenN :: (SplitGen g) => Int -> g -> [g]
+    splitGenN n g
+      | n < 1 = []
+      | otherwise = let (g1, g2) = splitGen g in g1 : splitGenN (n - 1) g2
 
 -- | See 'simulateAndFlattenMixtureModel', parallel version.
 simulateAndFlattenMixtureModelPar ::
-  (RandomGen g) =>
+  (SplitGen g) =>
   Int ->
   V.Vector Double ->
   V.Vector StationaryDistribution ->
