dataframe-learn 2.5.0.0 → 2.5.0.1
raw patch · 16 files changed
+230/−225 lines, 16 filesdep ~dataframe-coredep ~dataframe-expr-serializerdep ~dataframe-operationsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: dataframe-core, dataframe-expr-serializer, dataframe-operations
API changes (from Hackage documentation)
Files
- README.md +22/−23
- dataframe-learn.cabal +12/−12
- src-internal/DataFrame/DecisionTree/Cart.hs +5/−5
- src-internal/DataFrame/DecisionTree/CondVec.hs +27/−23
- src-internal/DataFrame/DecisionTree/Fit.hs +3/−2
- src-internal/DataFrame/DecisionTree/Pool.hs +20/−14
- src-internal/DataFrame/Featurize/Internal.hs +1/−1
- src/DataFrame/DBSCAN.hs +1/−2
- src/DataFrame/Transform.hs +2/−3
- tests-internal/DecisionTree.hs +73/−73
- tests-internal/Learn/EdgeCases.hs +10/−10
- tests-internal/Learn/NumericalRigor.hs +4/−5
- tests-internal/Learn/Symbolic.hs +6/−6
- tests-internal/Properties/Simplify.hs +16/−16
- tests-internal/TreePruning.hs +18/−19
- tests-internal/Worklist.hs +10/−11
README.md view
@@ -41,7 +41,7 @@ , "y" =: [2 * x + 1 | x <- [1, 2, 3, 4, 5, 6]] :: [Double] ] -model = fit defaultLinearConfig (D.col @Double "y") sales+model = fit defaultLinearConfig (D.double "y") sales putStrLn (D.prettyPrint (predict model)) ``` @@ -115,7 +115,7 @@ , "species" =: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2] :: [Double] ] -tree = fit defaultTreeConfig (D.col @Double "species") flowers+tree = fit defaultTreeConfig (D.double "species") flowers putStrLn (D.prettyPrint (predict tree)) ``` @@ -180,7 +180,7 @@ sr = fit defaultSRConfig { srSeed = 3, srGenerations = 50, srPopSize = 300, srUnaryOps = [] }- (D.col @Double "y") curve+ (D.double "y") curve putStrLn (D.prettyPrint (srBest sr) ++ " (mse " ++ show (srBestMSE sr) ++ ")") ``` @@ -198,7 +198,6 @@ > <!-- scripths:mime text/plain --> > <interactive>:87:47: error: [GHC-88464] > Variable not in scope: model-> Suggested fix: Perhaps use ‘T.mode’ (imported from DataFrame.Typed) > > <interactive>:87:54: error: [GHC-88464] > Variable not in scope: sales :: D.DataFrame@@ -215,7 +214,7 @@ ```haskell scaler = standardScaler ["x"] sales scaledSales = applyTransform (scalerTransform scaler) sales-scaledModel = fit defaultLinearConfig (D.col @Double "y") scaledSales+scaledModel = fit defaultLinearConfig (D.double "y") scaledSales deployed = compileThrough (scalerTransform scaler) (predict scaledModel) putStr (unlines@@ -242,14 +241,14 @@ original frame with no preprocessing step at inference time. ```haskell-evaluate rmse deployed (D.col @Double "y") sales+evaluate rmse deployed (D.double "y") sales ``` > <!-- scripths:mime text/plain --> > <interactive>:112:15: error: [GHC-88464] > Variable not in scope: deployed :: D.Expr Double > -> <interactive>:112:44: error: [GHC-88464]+> <interactive>:112:39: error: [GHC-88464] > Variable not in scope: sales :: D.DataFrame ## Splitting the data, and evaluation@@ -279,10 +278,10 @@ import System.Random (mkStdGen) (train, test) = D.randomSplit (mkStdGen 7) 0.75 clean-heldModel = fit defaultLinearConfig (D.col @Double "y") train+heldModel = fit defaultLinearConfig (D.double "y") train putStr (unlines- [ "held-out R^2: " ++ show (evaluate r2 (predict heldModel) (D.col @Double "y") test)- , "held-out RMSE: " ++ show (evaluate rmse (predict heldModel) (D.col @Double "y") test) ])+ [ "held-out R^2: " ++ show (evaluate r2 (predict heldModel) (D.double "y") test)+ , "held-out RMSE: " ++ show (evaluate rmse (predict heldModel) (D.double "y") test) ]) ``` > <!-- scripths:mime text/plain -->@@ -294,8 +293,8 @@ fold. You pass a `train -> Expr` closure, so it works with any model: ```haskell-cv = crossValidate 5 0 rmse (D.col @Double "y")- (\tr -> predict (fit defaultLinearConfig (D.col @Double "y") tr))+cv = crossValidate 5 0 rmse (D.double "y")+ (\tr -> predict (fit defaultLinearConfig (D.double "y") tr)) clean putStrLn ("5-fold RMSE: " ++ show (sum cv / fromIntegral (length cv))) ```@@ -313,17 +312,17 @@ macro/weighted averages): ```haskell-clf = fit defaultLogisticConfig (D.col @Double "species") flowers-putStr (show (classificationReportExpr (predict clf) (D.col @Double "species") flowers))+clf = fit defaultLogisticConfig (D.double "species") flowers+putStr (show (classificationReportExpr (predict clf) (D.double "species") flowers)) ``` > <!-- scripths:mime text/plain -->-> <interactive>:170:59: error: [GHC-88464]+> <interactive>:170:54: error: [GHC-88464] > Variable not in scope: flowers :: D.DataFrame > > <interactive>:173:49: error: [GHC-88464] Variable not in scope: clf > -> <interactive>:173:80: error: [GHC-88464]+> <interactive>:173:75: error: [GHC-88464] > Variable not in scope: flowers :: D.DataFrame ## Pipelines compose as a monoid@@ -335,7 +334,7 @@ ```haskell features = ["petal_length", "petal_width"] scalerF = standardScaler features flowers-pca = fit (PCAConfig (NComp 2) True) (map (D.col @Double) features) flowers+pca = fit (PCAConfig (NComp 2) True) (map (D.double) features) flowers pipeline = scalerTransform scalerF <> pcaTransform pca D.columnNames (applyTransform pipeline flowers)@@ -345,7 +344,7 @@ > <interactive>:182:36: error: [GHC-88464] > Variable not in scope: flowers :: D.DataFrame > -> <interactive>:183:74: error: [GHC-88464]+> <interactive>:183:69: error: [GHC-88464] > Variable not in scope: flowers :: D.DataFrame > > <interactive>:188:31: error: [GHC-88464]@@ -373,17 +372,17 @@ as = [-1, -1, 1, 1, -2, 2, -2, 2] :: [Double] bs = [-1, 1, -1, 1, -2, -2, 2, 2] :: [Double] -rawModel = fit defaultLinearConfig (D.col @Double "y") interactions-feature = fit defaultSynthesisConfig (D.col @Double "y") interactions+rawModel = fit defaultLinearConfig (D.double "y") interactions+feature = fit defaultSynthesisConfig (D.double "y") interactions withFeat = D.derive "synth" (predict feature) interactions fitModel =- fit defaultLinearConfig (D.col @Double "y")+ fit defaultLinearConfig (D.double "y") (D.select ["synth", "y"] withFeat) putStr (unlines [ "discovered feature: " ++ D.prettyPrint (predict feature)- , "raw linear R^2: " ++ show (evaluate r2 (predict rawModel) (D.col @Double "y") interactions)- , "with synth feature: " ++ show (evaluate r2 (predict fitModel) (D.col @Double "y") withFeat)+ , "raw linear R^2: " ++ show (evaluate r2 (predict rawModel) (D.double "y") interactions)+ , "with synth feature: " ++ show (evaluate r2 (predict fitModel) (D.double "y") withFeat) ]) ```
dataframe-learn.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: dataframe-learn-version: 2.5.0.0+version: 2.5.0.1 synopsis: Interpretable, expression-returning machine learning for the dataframe ecosystem. description: A small scikit-learn-style ML library where every model returns both an@@ -62,8 +62,8 @@ containers >= 0.6.7 && < 0.10, parallel >= 3.3 && < 4, random >= 1.2 && < 2,- dataframe-core >= 2.6 && < 2.7,- dataframe-operations >= 2.6 && < 2.7,+ dataframe-core >= 2.7 && < 2.8,+ dataframe-operations >= 2.7 && < 2.8, text >= 2.1 && < 3, vector >= 0.13 && < 0.15, vector-algorithms >= 0.9 && < 0.11@@ -106,11 +106,11 @@ containers >= 0.6.7 && < 0.10, parallel >= 3.3 && < 4, random >= 1.2 && < 2,- dataframe-core >= 2.6 && < 2.7,- dataframe-core >= 2.6 && < 2.7,- dataframe-operations >= 2.6 && < 2.7,- dataframe-operations >= 2.6 && < 2.7,- dataframe-expr-serializer >= 1.3 && < 1.4,+ dataframe-core >= 2.7 && < 2.8,+ dataframe-core >= 2.7 && < 2.8,+ dataframe-operations >= 2.7 && < 2.8,+ dataframe-operations >= 2.7 && < 2.8,+ dataframe-expr-serializer >= 1.4 && < 1.5, dataframe-learn:internal, text >= 2.1 && < 3, vector >= 0.13 && < 0.15,@@ -144,13 +144,13 @@ aeson >= 0.11.0.0 && < 3, bytestring >= 0.11 && < 0.14, containers >= 0.6.7 && < 0.10,- dataframe-core >= 2.6 && < 2.7,- dataframe-core >= 2.6 && < 2.7,+ dataframe-core >= 2.7 && < 2.8,+ dataframe-core >= 2.7 && < 2.8, dataframe-csv >= 2.3 && < 2.4, dataframe-learn, dataframe-learn:internal,- dataframe-operations >= 2.6 && < 2.7,- dataframe-operations >= 2.6 && < 2.7,+ dataframe-operations >= 2.7 && < 2.8,+ dataframe-operations >= 2.7 && < 2.8, HUnit >= 1.6 && < 1.8, QuickCheck >= 2 && < 3, random >= 1 && < 2,
src-internal/DataFrame/DecisionTree/Cart.hs view
@@ -301,7 +301,7 @@ (Columnable b, VU.Unbox b) => T.Text -> Maybe Bitmap -> VU.Vector b -> [CartFeature] numericFeature c Nothing v = case testEquality (typeRep @b) (typeRep @Double) of- Just Refl -> [CartFeature v Nothing (\t _ -> F.col @Double c .<=. F.lit t)]+ Just Refl -> [CartFeature v Nothing (\t _ -> F.double c .<=. F.lit t)] Nothing -> case sIntegral @b of STrue -> [ CartFeature@@ -327,9 +327,9 @@ T.Text -> Maybe (b -> Double, Double -> Expr (Maybe Bool)) nullableLeq c | Just Refl <- testEquality (typeRep @b) (typeRep @Double) =- Just (id, \t -> F.col @(Maybe Double) c .<= F.lit t)+ Just (id, \t -> F.maybeDouble c .<= F.lit t) | Just Refl <- testEquality (typeRep @b) (typeRep @Int) =- Just (fromIntegral, \t -> F.col @(Maybe Int) c .<= F.lit t)+ Just (fromIntegral, \t -> F.maybeInt c .<= F.lit t) | otherwise = Nothing oneHotFeatures ::@@ -348,7 +348,7 @@ CartFeature (VU.generate (V.length v) (\i -> if v V.! i == cat then 1 else 0)) Nothing- (\_ _ -> F.col @T.Text c ./=. F.lit cat)+ (\_ _ -> F.text c ./=. F.lit cat) oneHot c (Just nulls) v cat = CartFeature (VU.generate (V.length v) value)@@ -359,7 +359,7 @@ | nulls VU.! i = 1 / 0 | v V.! i == cat = 1 | otherwise = 0- col' = F.col @(Maybe T.Text) c+ col' = F.maybeText c -- Threshold 1 passes every non-null row, so that split is null vs non-null. cond t side | t < 1 = F.fromMaybe (side == NullsLeft) (col' ./= F.lit cat)
src-internal/DataFrame/DecisionTree/CondVec.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -24,10 +23,12 @@ import DataFrame.Internal.Column (TypedColumn (..), toVector) import DataFrame.Internal.DataFrame (DataFrame) import DataFrame.Internal.Expression (- BinaryOp (binaryName),+ BinOpId (..),+ BinaryOp (binaryId), Expr (..),+ builtinOf, eqExpr,- normalize,+ structuralKey, ) import DataFrame.Internal.Interpreter (interpret) @@ -57,13 +58,14 @@ -- | Truth vectors keyed by structural form, read-only once built. type CondCache = M.Map T.Text (VU.Vector Bool) --- | Structural key matching the candidate-dedup key.-condCacheKey :: Expr Bool -> T.Text-condCacheKey = T.pack . show . normalize+-- | Structural key; Nothing for conditions with anonymous operators.+condCacheKey :: Expr Bool -> Maybe T.Text+condCacheKey = fmap T.pack . structuralKey -- | Seed a cache from already-materialized candidate 'CondVec's (no interpret). condCacheFromVecs :: [CondVec] -> CondCache-condCacheFromVecs cvs = M.fromList [(condCacheKey (cvExpr cv), cvVec cv) | cv <- cvs]+condCacheFromVecs cvs =+ M.fromList [(k, cvVec cv) | cv <- cvs, Just k <- [condCacheKey (cvExpr cv)]] -- | Add a tree's branch-condition vectors to a cache. addTreeCondsToCache :: DataFrame -> Tree a -> CondCache -> CondCache@@ -73,16 +75,16 @@ go (Branch cond l r) c = go r (go l (insertCond df cond c)) insertCond :: DataFrame -> Expr Bool -> CondCache -> CondCache-insertCond df cond c- | M.member k c = c- | otherwise =- maybe c (\cv -> M.insert k (cvVec cv) c) (materializeCondVec df cond)- where- k = condCacheKey cond+insertCond df cond c = case condCacheKey cond of+ Nothing -> c+ Just k+ | M.member k c -> c+ | otherwise ->+ maybe c (\cv -> M.insert k (cvVec cv) c) (materializeCondVec df cond) -- | A condition's truth vector: a cache hit, else interpret. lookupCondVec :: CondCache -> DataFrame -> Expr Bool -> Maybe (VU.Vector Bool)-lookupCondVec cache df cond = case M.lookup (condCacheKey cond) cache of+lookupCondVec cache df cond = case (`M.lookup` cache) =<< condCacheKey cond of hit@(Just _) -> hit Nothing -> cvVec <$> materializeCondVec df cond @@ -99,7 +101,7 @@ -- | A same-column same-direction Double threshold comparison. data ThreshCmp = ThreshCmp { tcCol :: !T.Text- , tcName :: !T.Text+ , tcOp :: !BinOpId , tcThr :: !Double , tcRebuild :: Double -> Expr Bool }@@ -108,29 +110,31 @@ asDoubleThreshold (Binary op (Col c :: Expr cc) (Lit (t :: tt))) = case ( testEquality (typeRep @cc) (typeRep @Double) , testEquality (typeRep @tt) (typeRep @Double)+ , builtinOf (binaryId op) ) of- (Just Refl, Just Refl) -> Just (ThreshCmp c (binaryName op) t (Binary op (Col c) . Lit))+ (Just Refl, Just Refl, Just tag) ->+ Just (ThreshCmp c tag t (Binary op (Col c) . Lit)) _ -> Nothing asDoubleThreshold _ = Nothing -directionalNames :: [T.Text]-directionalNames = ["lt", "leq", "gt", "geq"]+directionalOps :: [BinOpId]+directionalOps = [BLt, BLeq, BGt, BGeq] -- | Tighter (AND) or looser (OR) of two same-direction thresholds.-chooseThreshold :: Bool -> T.Text -> Double -> Double -> Double-chooseThreshold isAnd name t1 t2+chooseThreshold :: Bool -> BinOpId -> Double -> Double -> Double+chooseThreshold isAnd op t1 t2 | leftDir = if isAnd then min t1 t2 else max t1 t2 | otherwise = if isAnd then max t1 t2 else min t1 t2 where- leftDir = name == "lt" || name == "leq"+ leftDir = op == BLt || op == BLeq -- | Collapse two same-direction Double comparisons into one, if possible. consolidateThreshold :: Bool -> Expr Bool -> Expr Bool -> Maybe (Expr Bool) consolidateThreshold isAnd ea eb = do a <- asDoubleThreshold ea b <- asDoubleThreshold eb- if tcCol a == tcCol b && tcName a == tcName b && tcName a `elem` directionalNames- then Just (tcRebuild a (chooseThreshold isAnd (tcName a) (tcThr a) (tcThr b)))+ if tcCol a == tcCol b && tcOp a == tcOp b && tcOp a `elem` directionalOps+ then Just (tcRebuild a (chooseThreshold isAnd (tcOp a) (tcThr a) (tcThr b))) else Nothing -- | AND-combine two cached conditions, consolidating thresholds if possible.
src-internal/DataFrame/DecisionTree/Fit.hs view
@@ -44,7 +44,8 @@ import DataFrame.Internal.Expression (Expr (..)) import DataFrame.Internal.Interpreter (interpret) import DataFrame.Operations.Core (nRows)-import DataFrame.Operations.Subset (exclude, filterWhere)+import DataFrame.Operations.Subset (exclude)+import qualified DataFrame.Operations.Subset as Subset import Control.Exception (throw) import Data.Function (on)@@ -101,7 +102,7 @@ pruneTree = pruneExpr partitionDataFrame :: Expr Bool -> DataFrame -> (DataFrame, DataFrame)-partitionDataFrame cond df = (filterWhere cond df, filterWhere (F.not cond) df)+partitionDataFrame cond df = (Subset.filter cond df, Subset.filter (F.not cond) df) -- | Laplace-smoothed Gini impurity of the target distribution. calculateGini ::
src-internal/DataFrame/DecisionTree/Pool.hs view
@@ -36,7 +36,7 @@ eSize, eqExpr, getColumns,- normalize,+ structuralKey, ) import Control.Parallel.Strategies (parListChunk, rdeepseq, using)@@ -157,12 +157,15 @@ admitKeys = go [] where go acc seen [] = (reverse acc, seen)- go acc !seen (c : cs)- | structuralKey c `Set.member` seen = go acc seen cs- | otherwise = go (c : acc) (Set.insert (structuralKey c) seen) cs+ go acc !seen (c : cs) = case cvKey c of+ Nothing -> go (c : acc) seen cs+ Just k+ | k `Set.member` seen -> go acc seen cs+ | otherwise -> go (c : acc) (Set.insert k seen) cs -structuralKey :: CondVec -> String-structuralKey = show . normalize . cvExpr+-- | Nothing for anonymous operators, which are never merged.+cvKey :: CondVec -> Maybe String+cvKey = structuralKey . cvExpr -- | Admit candidates by distinct truth vector, keeping the smallest expression. admitVecs ::@@ -187,17 +190,20 @@ dedupCVByExpr = go Set.empty where go _ [] = []- go seen (cv : cvs)- | structuralKey cv `Set.member` seen = go seen cvs- | otherwise = cv : go (Set.insert (structuralKey cv) seen) cvs+ go seen (cv : cvs) = case cvKey cv of+ Nothing -> cv : go seen cvs+ Just k+ | k `Set.member` seen -> go seen cvs+ | otherwise -> cv : go (Set.insert k seen) cvs -- | Deduplicate expressions by normalized form, keeping the first. nubByExpr :: [Expr Bool] -> [Expr Bool] nubByExpr = go Set.empty where+ go :: Set.Set String -> [Expr Bool] -> [Expr Bool] go _ [] = []- go seen (e : es)- | k `Set.member` seen = go seen es- | otherwise = e : go (Set.insert k seen) es- where- k = show (normalize e)+ go seen (e : es) = case structuralKey e of+ Nothing -> e : go seen es+ Just k+ | k `Set.member` seen -> go seen es+ | otherwise -> e : go (Set.insert k seen) es
src-internal/DataFrame/Featurize/Internal.hs view
@@ -80,7 +80,7 @@ then throw (nullableInputError name (columnTypeString c)) else if D.hasElemType @Double c- then fromRight undefined (columnAsUnboxedVector (F.col @Double name) df)+ then fromRight undefined (columnAsUnboxedVector (F.double name) df) else throw ( asDoubleInputError
src/DataFrame/DBSCAN.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} -- | Density-based clustering (DBSCAN) with brute-force region queries.@@ -103,7 +102,7 @@ dbscanSurrogateExpr :: TreeConfig -> [Expr Double] -> DBSCANModel -> DataFrame -> Expr Int dbscanSurrogateExpr cfg features model df =- fitDecisionTree cfg (F.col @Int clusterCol) augmented+ fitDecisionTree cfg (F.int clusterCol) augmented where clusterCol = "__cluster__" cols = map (\e -> (columnExprName e, materializeColumn df e)) features
src/DataFrame/Transform.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-} -- | Fitted column transforms as a composable monoid. module DataFrame.Transform (@@ -66,7 +65,7 @@ ScalerModel (V.fromList names) (VU.fromList means) (VU.fromList stds) where cols = map column names- column n = case columnAsDoubleVector (F.col @Double n) df of+ column n = case columnAsDoubleVector (F.double n) df of Right v -> v Left e -> throw e means = [VU.sum c / fromIntegral (max 1 (VU.length c)) | c <- cols]@@ -84,7 +83,7 @@ scalerTransform :: ScalerModel -> Transform scalerTransform m = Transform- [ (n, SomeExpr ((F.col @Double n .-. F.lit mu) ./. F.lit sigma))+ [ (n, SomeExpr ((F.double n .-. F.lit mu) ./. F.lit sigma)) | (n, mu, sigma) <- zip3 (V.toList (smColumns m))
tests-internal/DecisionTree.hs view
@@ -91,7 +91,7 @@ -- x <= 2.5: True for idx 0,1 (→ left); False for idx 2,3 (→ right) splitCond :: Expr Bool-splitCond = F.col @Double "x" .<= F.lit (2.5 :: Double)+splitCond = F.double "x" .<= F.lit (2.5 :: Double) -- Pre-computed care points for the full fixture carePoints3 :: [CarePoint]@@ -216,14 +216,14 @@ countErrorsAllCorrect :: Test countErrorsAllCorrect = TestCase $ do let cps = [CarePoint 0 GoLeft, CarePoint 1 GoRight]- cond = F.col @Double "x" .<= F.lit (1.5 :: Double)+ cond = F.double "x" .<= F.lit (1.5 :: Double) errs = countCarePointErrors cond fixtureDF cps assertEqual "condition routes all care points correctly" 0 errs countErrorsAllWrong :: Test countErrorsAllWrong = TestCase $ do let cps = [CarePoint 0 GoLeft, CarePoint 1 GoRight]- cond = F.col @Double "x" .> F.lit (1.5 :: Double)+ cond = F.double "x" .> F.lit (1.5 :: Double) errs = countCarePointErrors cond fixtureDF cps assertEqual "reversed condition misroutes all care points" 2 errs @@ -260,8 +260,8 @@ -- Candidate conditions that bracket the decision boundary sepConds :: [Expr Bool] sepConds =- [ F.col @Double "x" .<= F.lit (10.5 :: Double)- , F.col @Double "x" .> F.lit (10.5 :: Double)+ [ F.double "x" .<= F.lit (10.5 :: Double)+ , F.double "x" .> F.lit (10.5 :: Double) ] testCfg :: TreeConfig@@ -276,7 +276,7 @@ wrongStump :: Tree T.Text wrongStump = Branch- (F.col @Double "x" .> F.lit (10.5 :: Double))+ (F.double "x" .> F.lit (10.5 :: Double)) (Leaf "pos") (Leaf "neg") @@ -324,7 +324,7 @@ taoDeadBranchNoCrash :: Test taoDeadBranchNoCrash = TestCase $ do- let badCond = F.col @Double "x" .<= F.lit (0.5 :: Double)+ let badCond = F.double "x" .<= F.lit (0.5 :: Double) indices = V.enumFromN 0 20 initTree = Branch badCond (Leaf "pos") (Leaf "neg") :: Tree T.Text result =@@ -348,20 +348,20 @@ taoRecoversSingleObliqueDerived = TestCase $ do let labelExpr = F.ifThenElse- ((F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double))+ ((F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)) (F.lit ("pos" :: T.Text)) (F.lit ("neg" :: T.Text)) df = D.derive @T.Text "label" labelExpr gridBaseDF indices = V.enumFromN 0 16 initTree = Branch- (F.col @Double "x" .<= F.lit (2.5 :: Double))+ (F.double "x" .<= F.lit (2.5 :: Double)) (Leaf "pos") (Leaf "neg") :: Tree T.Text conds =- [ (F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double)- , (F.col @Double "x" + F.col @Double "y") .> F.lit (4.5 :: Double)+ [ (F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)+ , (F.double "x" + F.double "y") .> F.lit (4.5 :: Double) ] cfg = defaultTreeConfig{taoIterations = 5, expressionPairs = 4, minLeafSize = 1} result = taoOptimize @T.Text cfg "label" conds df indices initTree@@ -375,10 +375,10 @@ taoRecoversNestedObliqueDerived = TestCase $ do let labelExpr = F.ifThenElse- ((F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double))+ ((F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)) (F.lit ("low" :: T.Text)) ( F.ifThenElse- ((F.col @Double "x" - F.col @Double "y") .<= F.lit (0.5 :: Double))+ ((F.double "x" - F.double "y") .<= F.lit (0.5 :: Double)) (F.lit "mid") (F.lit "high") )@@ -386,19 +386,19 @@ indices = V.enumFromN 0 16 initTree = Branch- (F.col @Double "x" .<= F.lit (1.5 :: Double))+ (F.double "x" .<= F.lit (1.5 :: Double)) (Leaf "low") ( Branch- (F.col @Double "y" .<= F.lit (3.5 :: Double))+ (F.double "y" .<= F.lit (3.5 :: Double)) (Leaf "mid") (Leaf "high") ) :: Tree T.Text conds =- [ (F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double)- , (F.col @Double "x" + F.col @Double "y") .> F.lit (4.5 :: Double)- , (F.col @Double "x" - F.col @Double "y") .<= F.lit (0.5 :: Double)- , (F.col @Double "x" - F.col @Double "y") .> F.lit (0.5 :: Double)+ [ (F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)+ , (F.double "x" + F.double "y") .> F.lit (4.5 :: Double)+ , (F.double "x" - F.double "y") .<= F.lit (0.5 :: Double)+ , (F.double "x" - F.double "y") .> F.lit (0.5 :: Double) ] cfg = defaultTreeConfig{taoIterations = 5, expressionPairs = 4, minLeafSize = 1} result = taoOptimize @T.Text cfg "label" conds df indices initTree@@ -413,17 +413,17 @@ obliqueAxisAlignedFixture = let labelExpr = F.ifThenElse- ((F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double))+ ((F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)) (F.lit ("pos" :: T.Text)) (F.lit ("neg" :: T.Text)) df = D.derive @T.Text "label" labelExpr gridBaseDF indices = V.enumFromN 0 16 axisConds =- [F.col @Double "x" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]- ++ [F.col @Double "y" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]+ [F.double "x" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]+ ++ [F.double "y" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]] initTree = Branch- (F.col @Double "x" .<= F.lit (2.5 :: Double))+ (F.double "x" .<= F.lit (2.5 :: Double)) (Leaf "pos") (Leaf "neg") :: Tree T.Text@@ -537,7 +537,7 @@ (V.fromList [Nothing, Just (5.0 :: Double)] :: V.Vector (Maybe Double)) ) ]- cond = F.fromMaybe False (F.col @(Maybe Double) "x" .<= F.lit (6.0 :: Double))+ cond = F.fromMaybe False (F.maybeDouble "x" .<= F.lit (6.0 :: Double)) (lft, rgt) = partitionIndices cond df (V.fromList [0, 1]) assertBool "null row (idx 0) routes to right (false) partition" (0 `V.elem` rgt) assertBool "Just 5.0 <= 6.0 routes to left (true) partition" (1 `V.elem` lft)@@ -597,7 +597,7 @@ [c] -> assertBool "cond is isNothing x"- (eqExpr c (F.isNothing (F.col @(Maybe Double) "x")))+ (eqExpr c (F.isNothing (F.maybeDouble "x"))) _ -> assertFailure "expected exactly one condition" assertBool "no missingness conds for a non-nullable DataFrame"@@ -609,7 +609,7 @@ assertBool "generateNumericConds contains isNothing x" ( any- (`eqExpr` F.isNothing (F.col @(Maybe Double) "x"))+ (`eqExpr` F.isNothing (F.maybeDouble "x")) (generateNumericConds defaultTreeConfig (D.exclude ["label"] nullsMixedDF)) ) @@ -620,7 +620,7 @@ defaultTreeConfig (D.exclude ["label"] nullsMixedDF) nullsMixedDF- isMissingCV cv = eqExpr (cvExpr cv) (F.isNothing (F.col @(Maybe Double) "x"))+ isMissingCV cv = eqExpr (cvExpr cv) (F.isNothing (F.maybeDouble "x")) expected = VU.fromList [False, True, False, False, True, False] case filter isMissingCV cvs of (cv : _) -> assertEqual "isNothing vector marks null slots" expected (cvVec cv)@@ -640,7 +640,7 @@ ) ] conds = generateNumericConds defaultTreeConfig{percentiles = [50]} df- leqAt t = F.fromMaybe False (F.col @(Maybe Double) "x" .<= F.lit (t :: Double))+ leqAt t = F.fromMaybe False (F.maybeDouble "x" .<= F.lit (t :: Double)) assertBool "median threshold from observed values (30)" (any (`eqExpr` leqAt 30) conds)@@ -667,7 +667,7 @@ let df = D.fromColumns [("s", packedFromTexts (Just [1, 3]) ["yes", "", "no", ""])]- isMissingS = F.isNothing (F.col @(Maybe T.Text) "s")+ isMissingS = F.isNothing (F.maybeText "s") conds = missingnessConditions df assertEqual "one missingness cond for nullable PackedText" 1 (length conds) case conds of@@ -834,10 +834,10 @@ taoRecoversNestedObliqueWithoutHint = TestCase $ do let labelExpr = F.ifThenElse- ((F.col @Double "x" + F.col @Double "y") .<= F.lit (4.5 :: Double))+ ((F.double "x" + F.double "y") .<= F.lit (4.5 :: Double)) (F.lit ("low" :: T.Text)) ( F.ifThenElse- ((F.col @Double "x" - F.col @Double "y") .<= F.lit (0.5 :: Double))+ ((F.double "x" - F.double "y") .<= F.lit (0.5 :: Double)) (F.lit "mid") (F.lit "high") )@@ -845,17 +845,17 @@ indices = V.enumFromN 0 16 initTree = Branch- (F.col @Double "x" .<= F.lit (1.5 :: Double))+ (F.double "x" .<= F.lit (1.5 :: Double)) (Leaf "low") ( Branch- (F.col @Double "y" .<= F.lit (3.5 :: Double))+ (F.double "y" .<= F.lit (3.5 :: Double)) (Leaf "mid") (Leaf "high") ) :: Tree T.Text axisOnlyConds =- [F.col @Double "x" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]- ++ [F.col @Double "y" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]+ [F.double "x" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]]+ ++ [F.double "y" .<= F.lit (t :: Double) | t <- [1.5, 2.5, 3.5]] cfg = defaultTreeConfig { taoIterations = 20@@ -1161,90 +1161,90 @@ threshAndLeq :: Test threshAndLeq = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .<=. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .<=. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .<=. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .<=. F.lit (1.0 :: Double)) r = combineAndVec a b assertEqExpr "AND of x≤3 and x≤1 collapses to x≤1"- (F.col @Double "x" .<=. F.lit (1.0 :: Double))+ (F.double "x" .<=. F.lit (1.0 :: Double)) (cvExpr r) threshOrLeq :: Test threshOrLeq = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .<=. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .<=. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .<=. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .<=. F.lit (1.0 :: Double)) r = combineOrVec a b assertEqExpr "OR of x≤3 and x≤1 collapses to x≤3"- (F.col @Double "x" .<=. F.lit (3.0 :: Double))+ (F.double "x" .<=. F.lit (3.0 :: Double)) (cvExpr r) threshAndLt :: Test threshAndLt = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .<. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .<. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .<. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .<. F.lit (1.0 :: Double)) r = combineAndVec a b assertEqExpr "AND of x<3 and x<1 collapses to x<1"- (F.col @Double "x" .<. F.lit (1.0 :: Double))+ (F.double "x" .<. F.lit (1.0 :: Double)) (cvExpr r) threshOrLt :: Test threshOrLt = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .<. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .<. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .<. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .<. F.lit (1.0 :: Double)) r = combineOrVec a b assertEqExpr "OR of x<3 and x<1 collapses to x<3"- (F.col @Double "x" .<. F.lit (3.0 :: Double))+ (F.double "x" .<. F.lit (3.0 :: Double)) (cvExpr r) threshAndGeq :: Test threshAndGeq = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>=. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>=. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>=. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .>=. F.lit (3.0 :: Double)) r = combineAndVec a b assertEqExpr "AND of x≥1 and x≥3 collapses to x≥3"- (F.col @Double "x" .>=. F.lit (3.0 :: Double))+ (F.double "x" .>=. F.lit (3.0 :: Double)) (cvExpr r) threshOrGeq :: Test threshOrGeq = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>=. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>=. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>=. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .>=. F.lit (3.0 :: Double)) r = combineOrVec a b assertEqExpr "OR of x≥1 and x≥3 collapses to x≥1"- (F.col @Double "x" .>=. F.lit (1.0 :: Double))+ (F.double "x" .>=. F.lit (1.0 :: Double)) (cvExpr r) threshAndGt :: Test threshAndGt = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .>. F.lit (3.0 :: Double)) r = combineAndVec a b assertEqExpr "AND of x>1 and x>3 collapses to x>3"- (F.col @Double "x" .>. F.lit (3.0 :: Double))+ (F.double "x" .>. F.lit (3.0 :: Double)) (cvExpr r) threshOrGt :: Test threshOrGt = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .>. F.lit (3.0 :: Double)) r = combineOrVec a b assertEqExpr "OR of x>1 and x>3 collapses to x>1"- (F.col @Double "x" .>. F.lit (1.0 :: Double))+ (F.double "x" .>. F.lit (1.0 :: Double)) (cvExpr r) -- Six negative cases: rewrite must NOT fire. threshNegMixedDirection :: Test threshNegMixedDirection = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .<. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>=. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .<. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .>=. F.lit (1.0 :: Double)) r = combineAndVec a b assertEqExpr "mixed-direction AND keeps generic F.and form"@@ -1253,8 +1253,8 @@ threshNegCrossColumn :: Test threshNegCrossColumn = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "y" .>. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "y" .>. F.lit (3.0 :: Double)) r = combineAndVec a b assertEqExpr "cross-column AND keeps generic F.and form"@@ -1263,8 +1263,8 @@ threshNegMixedOpFamily :: Test threshNegMixedOpFamily = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .<. F.lit (4.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .<. F.lit (4.0 :: Double)) r = combineAndVec a b assertEqExpr "different-op-family AND keeps generic F.and form"@@ -1273,8 +1273,8 @@ threshNegEqualityOp :: Test threshNegEqualityOp = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .==. F.lit (3.0 :: Double))- b = materializeOrFail (F.col @Double "x" .==. F.lit (1.0 :: Double))+ let a = materializeOrFail (F.double "x" .==. F.lit (3.0 :: Double))+ b = materializeOrFail (F.double "x" .==. F.lit (1.0 :: Double)) r = combineOrVec a b assertEqExpr "equality OR keeps generic F.or form"@@ -1283,8 +1283,8 @@ threshNegLitOnLeft :: Test threshNegLitOnLeft = TestCase $ do- let a = materializeOrFail (F.lit (1.0 :: Double) .<. F.col @Double "x")- b = materializeOrFail (F.lit (3.0 :: Double) .<. F.col @Double "x")+ let a = materializeOrFail (F.lit (1.0 :: Double) .<. F.double "x")+ b = materializeOrFail (F.lit (3.0 :: Double) .<. F.double "x") r = combineAndVec a b assertEqExpr "Lit-on-left AND keeps generic F.and form"@@ -1293,8 +1293,8 @@ threshNegNonLiteralRhs :: Test threshNegNonLiteralRhs = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.col @Double "y")- b = materializeOrFail (F.col @Double "x" .>. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.double "y")+ b = materializeOrFail (F.double "x" .>. F.lit (3.0 :: Double)) r = combineAndVec a b assertEqExpr "non-literal RHS AND keeps generic F.and form"@@ -1304,8 +1304,8 @@ -- The consolidated cvVec equals the elementwise AND/OR of the inputs. threshSemanticPreservation :: Test threshSemanticPreservation = TestCase $ do- let a = materializeOrFail (F.col @Double "x" .>. F.lit (1.0 :: Double))- b = materializeOrFail (F.col @Double "x" .>. F.lit (3.0 :: Double))+ let a = materializeOrFail (F.double "x" .>. F.lit (1.0 :: Double))+ b = materializeOrFail (F.double "x" .>. F.lit (3.0 :: Double)) rAnd = combineAndVec a b rOr = combineOrVec a b expectedAnd = VU.zipWith (&&) (cvVec a) (cvVec b)
tests-internal/Learn/EdgeCases.hs view
@@ -175,7 +175,7 @@ [ ("x", DI.fromList ([-3, -2, -1, -0.5, 0.5, 1, 2, 3] :: [Double])) , ("label", DI.fromList ([0, 0, 0, 0, 1, 1, 1, 1] :: [Int])) ]- m = fit defaultLogisticConfig (F.col @Int "label") trainDf+ m = fit defaultLogisticConfig (F.int "label") trainDf probs = logisticProbExprs m extremeDf = D.fromColumns@@ -196,7 +196,7 @@ [ ("x", DI.fromList ([2] :: [Double])) , ("y", DI.fromList ([7] :: [Double])) ]- m = fit defaultLinearConfig (F.col @Double "y") df+ m = fit defaultLinearConfig (F.double "y") df preds = interpD df (predict m) assertBool "single OLS prediction finite" (all finite preds) assertBool@@ -211,7 +211,7 @@ [ ("x", DI.fromList ([-2, -1, 0, 1, 2] :: [Double])) , ("label", DI.fromList ([1, 1, 1, 1, 1] :: [Int])) ]- m = fit defaultLogisticConfig (F.col @Int "label") df+ m = fit defaultLogisticConfig (F.int "label") df preds = interpI df (predict m) assertEqual "single-class logistic predicts that class everywhere"@@ -227,7 +227,7 @@ , ("const", DI.fromList (replicate 8 (5.0 :: Double))) , ("label", DI.fromList ([0, 0, 0, 0, 1, 1, 1, 1] :: [Int])) ]- m = fit defaultLogisticConfig (F.col @Int "label") df+ m = fit defaultLogisticConfig (F.int "label") df preds = interpI df (predict m) assertEqual "logistic ignores constant column and separates"@@ -244,7 +244,7 @@ , ("const", DI.fromList (replicate 6 (5.0 :: Double))) , ("y", DI.fromList [3 * x + 4 | x <- xs]) ]- m = fit defaultLinearConfig (F.col @Double "y") df+ m = fit defaultLinearConfig (F.double "y") df coefs = VU.toList (regCoef m) names = V.toList (regFeatureNames m) byName = zip names coefs@@ -266,7 +266,7 @@ , ("b", DI.fromList ([0, 5, 10] :: [Double])) ] cfg = defaultKMeansConfig{kmK = 10, kmNInit = 3, kmSeed = 1}- m = fit cfg [F.col @Double "a", F.col @Double "b"] df+ m = fit cfg [F.double "a", F.double "b"] df assertEqual "k clamped to n=3 centres" 3 (V.length (kmCenters m)) let labels = VU.toList (kmLabels m) assertBool "all labels in [0,2]" (all (\l -> l >= 0 && l < 3) labels)@@ -284,7 +284,7 @@ , ("b", DI.fromList (replicate 6 (4.0 :: Double))) ] cfg = defaultKMeansConfig{kmK = 2, kmNInit = 3, kmSeed = 1}- m = fit cfg [F.col @Double "a", F.col @Double "b"] df+ m = fit cfg [F.double "a", F.double "b"] df assertBool "centres finite on degenerate identical data" (all (VU.all finite) (V.toList (kmCenters m)))@@ -301,7 +301,7 @@ , ("b", DI.fromList ([0, 10] :: [Double])) ] cfg = defaultGMMConfig{gmmK = 5, gmmSeed = 1}- m = fit cfg [F.col @Double "a", F.col @Double "b"] df+ m = fit cfg [F.double "a", F.double "b"] df assertEqual "GMM k clamped to n=2" 2 (VU.length (gmmWeights m)) assertBool "GMM weights finite" (VU.all finite (gmmWeights m)) assertBool "GMM weights sum to ~1" (close 1e-6 (VU.sum (gmmWeights m)) 1)@@ -318,7 +318,7 @@ m = fit (PCAConfig (NComp 2) False)- [F.col @Double "x", F.col @Double "const"]+ [F.double "x", F.double "const"] df ratio = VU.toList (pcaExplainedVarianceRatio m) assertBool "explained ratios finite" (all finite ratio)@@ -343,7 +343,7 @@ m = fit (PCAConfig (NComp 1) False)- [F.col @Double "x", F.col @Double "y"]+ [F.double "x", F.double "y"] df comp0 = pcaComponents m V.! 0 nrm = sqrt (VU.sum (VU.map (^ (2 :: Int)) comp0))
tests-internal/Learn/NumericalRigor.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-} -- | Numerical-rigor suite: gradient checks, reproducibility, RNG properties. module Learn.NumericalRigor (tests) where@@ -206,7 +205,7 @@ kmInertia $ fit defaultKMeansConfig{kmK = 2, kmNInit = 1, kmSeed = s}- [F.col @Double "a", F.col @Double "b"]+ [F.double "a", F.double "b"] df inertias = map fitSeed [0 .. 19] blob1 = take 5 (zip as bs)@@ -261,7 +260,7 @@ testKMeansReproducible :: Test testKMeansReproducible = TestCase $ do let cfg s = defaultKMeansConfig{kmK = 4, kmNInit = 1, kmMaxIter = 1, kmSeed = s}- run s = fit (cfg s) [F.col @Double "a", F.col @Double "b"]+ run s = fit (cfg s) [F.double "a", F.double "b"] a = run 1 spreadDF b = run 1 spreadDF assertEqual "k-means same seed identical model" a b@@ -274,7 +273,7 @@ testGMMReproducible :: Test testGMMReproducible = TestCase $ do let cfg s = defaultGMMConfig{gmmK = 2, gmmMaxIter = 1, gmmSeed = s}- run s = fit (cfg s) [F.col @Double "a", F.col @Double "b"] blobsDF+ run s = fit (cfg s) [F.double "a", F.double "b"] blobsDF a = run 1 b = run 1 assertEqual "GMM same seed identical model" a b@@ -292,7 +291,7 @@ , ("label", DI.fromList ([0, 0, 0, 0, 1, 1, 1, 1] :: [Int])) ] cfg s = defaultRFFConfig{rffD = 40, rffGamma = 0.2, rffSeed = s}- run s = fit (cfg s) (F.col @Int "label") clsDF+ run s = fit (cfg s) (F.int "label") clsDF a = run 5 b = run 5 assertEqual "RFF same seed: same projection B" (rffB a) (rffB b)
tests-internal/Learn/Symbolic.hs view
@@ -47,10 +47,10 @@ , srPopSize = 300 , srUnaryOps = [] }- (F.col @Double "y")+ (F.double "y") df preds = interpD df (srBest m)- truth = interpD df (F.col @Double "y")+ truth = interpD df (F.double "y") err = sum (zipWith (\p t -> (p - t) ^ (2 :: Int)) preds truth) / 10 assertBool "SR recovers x*x+x to low error" (err < 1e-6) assertBool "Pareto front non-empty" (not (null (srPareto m)))@@ -66,14 +66,14 @@ run = fit defaultSRConfig{srSeed = 9, srGenerations = 20}- (F.col @Double "y")+ (F.double "y") df assertEqual "SR deterministic best MSE" (srBestMSE run) (srBestMSE (rerun df)) where rerun = fit defaultSRConfig{srSeed = 9, srGenerations = 20}- (F.col @Double "y")+ (F.double "y") testSimplifyPreservesEval :: Test testSimplifyPreservesEval = TestCase $ do@@ -100,7 +100,7 @@ m = fit defaultKernelPCAConfig{kpcaNComponents = 2, kpcaNLandmarks = 8, kpcaSeed = 1}- [F.col @Double "a", F.col @Double "b"]+ [F.double "a", F.double "b"] df pc1 = case kernelPCAExprs m of ((_, e) : _) -> interpD df e@@ -123,7 +123,7 @@ m = fit defaultRFFConfig{rffD = 80, rffGamma = 0.2, rffSeed = 2}- (F.col @Int "label")+ (F.int "label") df preds = interpI df (predict m) assertEqual "RFF SVM separates" [0, 0, 0, 0, 1, 1, 1, 1] preds
tests-internal/Properties/Simplify.hs view
@@ -61,18 +61,18 @@ t <- elements thresholds oneof [ elements- [ F.col @Double "x" .< F.lit t- , F.col @Double "x" .<= F.lit t- , F.col @Double "x" .> F.lit t- , F.col @Double "x" .>= F.lit t- , F.col @Double "x" .== F.lit t- , F.col @Double "x" ./= F.lit t+ [ F.double "x" .< F.lit t+ , F.double "x" .<= F.lit t+ , F.double "x" .> F.lit t+ , F.double "x" .>= F.lit t+ , F.double "x" .== F.lit t+ , F.double "x" ./= F.lit t ] , elements- [ F.toDouble (F.col @Int "n") .< F.lit t- , F.toDouble (F.col @Int "n") .<= F.lit t- , F.toDouble (F.col @Int "n") .> F.lit t- , F.toDouble (F.col @Int "n") .>= F.lit t+ [ F.toDouble (F.int "n") .< F.lit t+ , F.toDouble (F.int "n") .<= F.lit t+ , F.toDouble (F.int "n") .> F.lit t+ , F.toDouble (F.int "n") .>= F.lit t ] ] @@ -92,12 +92,12 @@ genAtomMaybe = do t <- elements thresholds elements- [ F.col @(Maybe Double) "m" .< F.lit t- , F.col @(Maybe Double) "m" .<= F.lit t- , F.col @(Maybe Double) "m" .> F.lit t- , F.col @(Maybe Double) "m" .>= F.lit t- , F.col @(Maybe Double) "m" .== F.lit t- , F.col @(Maybe Double) "m" ./= F.lit t+ [ F.maybeDouble "m" .< F.lit t+ , F.maybeDouble "m" .<= F.lit t+ , F.maybeDouble "m" .> F.lit t+ , F.maybeDouble "m" .>= F.lit t+ , F.maybeDouble "m" .== F.lit t+ , F.maybeDouble "m" ./= F.lit t ] genMaybeExpr :: Int -> Gen (Expr (Maybe Bool))
tests-internal/TreePruning.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-} -- | Specification for the fitted-tree pruning pass (pruneDead). module TreePruning (tests) where@@ -33,19 +32,19 @@ [ prunesTo "ancestor entails child keeps true subtree" ( Branch- (F.col @Double "age" .> F.lit (50 :: Double))- (Branch (F.col @Double "age" .> F.lit (30 :: Double)) (Leaf "a") (Leaf "b"))+ (F.double "age" .> F.lit (50 :: Double))+ (Branch (F.double "age" .> F.lit (30 :: Double)) (Leaf "a") (Leaf "b")) (Leaf "c") )- (Branch (F.col @Double "age" .> F.lit (50 :: Double)) (Leaf "a") (Leaf "c"))+ (Branch (F.double "age" .> F.lit (50 :: Double)) (Leaf "a") (Leaf "c")) , prunesTo "ancestor refutes child keeps false subtree" ( Branch- (F.col @Double "age" .> F.lit (50 :: Double))- (Branch (F.col @Double "age" .< F.lit (40 :: Double)) (Leaf "a") (Leaf "b"))+ (F.double "age" .> F.lit (50 :: Double))+ (Branch (F.double "age" .< F.lit (40 :: Double)) (Leaf "a") (Leaf "b")) (Leaf "c") )- (Branch (F.col @Double "age" .> F.lit (50 :: Double)) (Leaf "b") (Leaf "c"))+ (Branch (F.double "age" .> F.lit (50 :: Double)) (Leaf "b") (Leaf "c")) ] falseEdgeGate :: [Test]@@ -53,16 +52,16 @@ [ prunesTo "integral false edge entails child" ( Branch- (F.toDouble (F.col @Int "ai") .> F.lit (50 :: Double))+ (F.toDouble (F.int "ai") .> F.lit (50 :: Double)) (Leaf "c") ( Branch- (F.toDouble (F.col @Int "ai") .< F.lit (60 :: Double))+ (F.toDouble (F.int "ai") .< F.lit (60 :: Double)) (Leaf "a") (Leaf "b") ) ) ( Branch- (F.toDouble (F.col @Int "ai") .> F.lit (50 :: Double))+ (F.toDouble (F.int "ai") .> F.lit (50 :: Double)) (Leaf "c") (Leaf "a") )@@ -72,13 +71,13 @@ sameBranchCollapse = [ prunesTo "equal leaves collapse the branch"- (Branch (F.col @Double "age" .> F.lit (50 :: Double)) (Leaf "a") (Leaf "a"))+ (Branch (F.double "age" .> F.lit (50 :: Double)) (Leaf "a") (Leaf "a")) (Leaf "a") , prunesTo "collapse cascades upward" ( Branch- (F.col @Double "age" .> F.lit (50 :: Double))- (Branch (F.col @Double "hours" .> F.lit (40 :: Double)) (Leaf "a") (Leaf "a"))+ (F.double "age" .> F.lit (50 :: Double))+ (Branch (F.double "hours" .> F.lit (40 :: Double)) (Leaf "a") (Leaf "a")) (Leaf "a") ) (Leaf "a")@@ -89,22 +88,22 @@ [ preserved "child not tight enough is kept" ( Branch- (F.col @Double "age" .> F.lit (50 :: Double))- (Branch (F.col @Double "age" .> F.lit (60 :: Double)) (Leaf "a") (Leaf "b"))+ (F.double "age" .> F.lit (50 :: Double))+ (Branch (F.double "age" .> F.lit (60 :: Double)) (Leaf "a") (Leaf "b")) (Leaf "c") ) , preserved "double false edge is kept (NaN)" ( Branch- (F.col @Double "weight" .> F.lit (50 :: Double))+ (F.double "weight" .> F.lit (50 :: Double)) (Leaf "c")- (Branch (F.col @Double "weight" .< F.lit (60 :: Double)) (Leaf "a") (Leaf "b"))+ (Branch (F.double "weight" .< F.lit (60 :: Double)) (Leaf "a") (Leaf "b")) ) , preserved "cross-column descendant is kept" ( Branch- (F.col @Double "age" .> F.lit (50 :: Double))- (Branch (F.col @Double "income" .> F.lit (30000 :: Double)) (Leaf "a") (Leaf "b"))+ (F.double "age" .> F.lit (50 :: Double))+ (Branch (F.double "income" .> F.lit (30000 :: Double)) (Leaf "a") (Leaf "b")) (Leaf "c") ) ]
tests-internal/Worklist.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-} -- | Spec for the saturateCandidates worklist against the boolExprsVec oracle. module Worklist (tests, props) where@@ -51,16 +50,16 @@ (materializeCondVec fixtureDF e) xGt, xLt, yGt, yLt, zGt, zLt :: Double -> CondVec-xGt n = mat (F.col @Double "x" .>. F.lit n)-xLt n = mat (F.col @Double "x" .<. F.lit n)-yGt n = mat (F.col @Double "y" .>. F.lit n)-yLt n = mat (F.col @Double "y" .<. F.lit n)-zGt n = mat (F.col @Double "z" .>. F.lit n)-zLt n = mat (F.col @Double "z" .<. F.lit n)+xGt n = mat (F.double "x" .>. F.lit n)+xLt n = mat (F.double "x" .<. F.lit n)+yGt n = mat (F.double "y" .>. F.lit n)+yLt n = mat (F.double "y" .<. F.lit n)+zGt n = mat (F.double "z" .>. F.lit n)+zLt n = mat (F.double "z" .<. F.lit n) -- Same truth vector as 'xGt 2' but eSize 4 vs 3: a non-degenerate collision. notLe2 :: CondVec-notLe2 = mat (F.not (F.col @Double "x" .<=. F.lit 2))+notLe2 = mat (F.not (F.double "x" .<=. F.lit 2)) litTrue :: CondVec litTrue = mat (F.lit True)@@ -113,9 +112,9 @@ wideBase :: [CondVec] wideBase =- [ matW (F.col @Double "a" .>. F.lit 3)- , matW (F.col @Double "b" .<. F.lit 5)- , matW (F.col @Double "c" .>=. F.lit 3)+ [ matW (F.double "a" .>. F.lit 3)+ , matW (F.double "b" .<. F.lit 5)+ , matW (F.double "c" .>=. F.lit 3) ] tests :: [Test]