From 208a73aae509790c8c89c9ff2d218fc28cf0489c Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 01:30:59 -0600 Subject: [PATCH 01/22] add flake --- flake.nix | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 flake.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..57b1c63 --- /dev/null +++ b/flake.nix @@ -0,0 +1,66 @@ +# https://community.flake.parts/haskell-flake/dependency +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + haskell-flake.url = "github:srid/haskell-flake"; + }; + outputs = inputs@{ self, nixpkgs, flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = nixpkgs.lib.systems.flakeExposed; + imports = [ inputs.haskell-flake.flakeModule ]; + + perSystem = { self', pkgs, lib, ... }: { + + # Typically, you just want a single project named "default". But + # multiple projects are also possible, each using different GHC version. + haskellProjects.default = { + # The base package set representing a specific GHC version. + # By default, this is pkgs.haskellPackages. + # You may also create your own. See https://community.flake.parts/haskell-flake/package-set + # basePackages = pkgs.haskellPackages; + + # Extra package information. See https://community.flake.parts/haskell-flake/dependency + # + # Note that local packages are automatically included in `packages` + # (defined by `defaults.packages` option). + # + projectRoot = builtins.toString (lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./src + ./haskell-template.cabal + ]; + }); + packages = { + # aeson.source = "1.5.0.0"; # Override aeson to a custom version from Hackage + # shower.source = inputs.shower; # Override shower to a custom source path + }; + settings = { + # aeson = { + # check = false; + # }; + # relude = { + # haddock = false; + # broken = false; + # }; + }; + + devShell = { + # Enabled by default + # enable = true; + + # Programs you want to make available in the shell. + # Default programs can be disabled by setting to 'null' + # tools = hp: { fourmolu = hp.fourmolu; ghcid = null; }; + + # Check that haskell-language-server works + # hlsCheck.enable = true; # Requires sandbox to be disabled + }; + }; + + # haskell-flake doesn't set the default package, but you can do it here. + packages.default = self'.packages.HushGP; + }; + }; +} From 780e23e93bbf3eef21a43aaae7136406eb1e91cc Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 01:32:12 -0600 Subject: [PATCH 02/22] add flake.log --- flake.lock | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 flake.lock diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..246ac6b --- /dev/null +++ b/flake.lock @@ -0,0 +1,74 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "haskell-flake": { + "locked": { + "lastModified": 1740325125, + "narHash": "sha256-isWfa0ZxT4BLd00uVgBGZ4YHahODtQkc564ooHBSxmU=", + "owner": "srid", + "repo": "haskell-flake", + "rev": "4e9d319e78c00511cfbd48cc5ef4fb9c4df0dd95", + "type": "github" + }, + "original": { + "owner": "srid", + "repo": "haskell-flake", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1740396192, + "narHash": "sha256-ATMHHrg3sG1KgpQA5x8I+zcYpp5Sf17FaFj/fN+8OoQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "d9b69c3ec2a2e2e971c534065bdd53374bd68b97", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1738452942, + "narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "haskell-flake": "haskell-flake", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} From 158172a6aea0d0f0e8fa7949cfc83f43d759b5cc Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 01:34:49 -0600 Subject: [PATCH 03/22] uncomment a config line --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 57b1c63..3a87045 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ # The base package set representing a specific GHC version. # By default, this is pkgs.haskellPackages. # You may also create your own. See https://community.flake.parts/haskell-flake/package-set - # basePackages = pkgs.haskellPackages; + basePackages = pkgs.haskellPackages; # Extra package information. See https://community.flake.parts/haskell-flake/dependency # From c7d0927b3fa5cb76cdd2806239413cb4632ed892 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 01:36:09 -0600 Subject: [PATCH 04/22] comment out project root stuff --- flake.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 3a87045..c27d857 100644 --- a/flake.nix +++ b/flake.nix @@ -25,13 +25,13 @@ # Note that local packages are automatically included in `packages` # (defined by `defaults.packages` option). # - projectRoot = builtins.toString (lib.fileset.toSource { - root = ./.; - fileset = lib.fileset.unions [ - ./src - ./haskell-template.cabal - ]; - }); + # projectRoot = builtins.toString (lib.fileset.toSource { + # root = ./.; + # fileset = lib.fileset.unions [ + # ./src + # ./haskell-template.cabal + # ]; + # }); packages = { # aeson.source = "1.5.0.0"; # Override aeson to a custom version from Hackage # shower.source = inputs.shower; # Override shower to a custom source path From 5e86915bd15511cd5dd93a58958bb8d88089eac1 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 01:44:21 -0600 Subject: [PATCH 05/22] try ghc982 --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index c27d857..73ef7e8 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ # The base package set representing a specific GHC version. # By default, this is pkgs.haskellPackages. # You may also create your own. See https://community.flake.parts/haskell-flake/package-set - basePackages = pkgs.haskellPackages; + basePackages = pkgs.haskell.packages.ghc982; # Extra package information. See https://community.flake.parts/haskell-flake/dependency # From 6ca07694e6bbef87311d74fad71c32fd4bfae7b0 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 03:14:03 -0600 Subject: [PATCH 06/22] change to HushGP.cabal --- flake.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 73ef7e8..df696c0 100644 --- a/flake.nix +++ b/flake.nix @@ -25,13 +25,13 @@ # Note that local packages are automatically included in `packages` # (defined by `defaults.packages` option). # - # projectRoot = builtins.toString (lib.fileset.toSource { - # root = ./.; - # fileset = lib.fileset.unions [ - # ./src - # ./haskell-template.cabal - # ]; - # }); + projectRoot = builtins.toString (lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./src + ./HushGP.cabal + ]; + }); packages = { # aeson.source = "1.5.0.0"; # Override aeson to a custom version from Hackage # shower.source = inputs.shower; # Override shower to a custom source path From 164765aa233e9174758d847616a5bc6a7a751d2b Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:02:39 -0600 Subject: [PATCH 07/22] comment out base gpLoop' --- src/HushGP/GP.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index 57215aa..f8c7c21 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -77,4 +77,4 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do bestIndPassesDownsample = False -- TODO: fix this later epsilonPushArgs :: PushArgs epsilonPushArgs = pushArgs {epsilons = Nothing} -- TODO: And this -gpLoop' _ _ _ _ _ = error "How did this happen?" +--gpLoop' _ _ _ _ _ = error "How did this happen?" From 538178ccfa3f2f9db820b946e9c7dc6952ce5186 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:15:15 -0600 Subject: [PATCH 08/22] start of [individual] --- src/HushGP/GP.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index f8c7c21..f805a80 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -58,6 +58,7 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do print "Incomplete Run, saving the best so far." | otherwise = gpLoop' pushArgs (succ generation) (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) + (if elitism then ) nextAction where -- \| This will have downsampling added to it later. From 6e9b9a4827fa6280ddbbdf0546f96444208a1a6b Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:17:29 -0600 Subject: [PATCH 09/22] ormolu and threadscope --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index df696c0..e6bb793 100644 --- a/flake.nix +++ b/flake.nix @@ -52,7 +52,7 @@ # Programs you want to make available in the shell. # Default programs can be disabled by setting to 'null' - # tools = hp: { fourmolu = hp.fourmolu; ghcid = null; }; + tools = hp: { ormolu = hp.ormolu; threadscope = hp.threadscope; }; # Check that haskell-language-server works # hlsCheck.enable = true; # Requires sandbox to be disabled From 0c1d2a5d507fa9e818ee8dde76854a4896595e11 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:28:18 -0600 Subject: [PATCH 10/22] Nix mention --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d9e2b3c..44dd86c 100644 --- a/README.md +++ b/README.md @@ -45,3 +45,7 @@ This is clearly not ideal. 4) For the exec stack itself, typeable, data generic, ghc.generic, data.dynamic, heterogeneous lists, etc. could also help, to detect the type of variables at runtime, but I would rather stick to language basics at first. + +## Nix Users + +This took my machine about 2 hours to build the environment after running `nix develop`. From 52ed502b61819e0dd759d37a49644b04b2de585b Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:59:04 -0600 Subject: [PATCH 11/22] balance close mention --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index fe3600d..58f001b 100644 --- a/TODO.md +++ b/TODO.md @@ -27,6 +27,7 @@ - I'm only going to implement propeller's :specified version - Is the best according to the papers - [X] Need a NoOp that opens blocks + - [ ] Have a way to balance amount of closes with open blocks - [ ] Need to make genomes serializable (Check pysh json files) - [ ] Add Memory - [ ] Add history stack(s), like a call stack From 747bf57d03270514cd6028653edf97a07b0d6421 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:59:23 -0600 Subject: [PATCH 12/22] add variation file --- HushGP.cabal | 1 + src/HushGP/GP/Variation.hs | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 src/HushGP/GP/Variation.hs diff --git a/HushGP.cabal b/HushGP.cabal index 87df5dc..d0eb20d 100644 --- a/HushGP.cabal +++ b/HushGP.cabal @@ -62,6 +62,7 @@ library , HushGP.PushTests.UtilTests , HushGP.GP , HushGP.GP.PushArgs + , HushGP.GP.Variation , HushGP.Problems.IntegerRegression diff --git a/src/HushGP/GP/Variation.hs b/src/HushGP/GP/Variation.hs new file mode 100644 index 0000000..a3439e9 --- /dev/null +++ b/src/HushGP/GP/Variation.hs @@ -0,0 +1,7 @@ +module HushGP.GP.Variation where + +import HushGP.Genome +import HushGP.GP.PushArgs + +newIndividual :: PushArgs -> [Individual] -> Individual +newIndividual = error "Implement this later" From dec879498fc0b74ba3da8e111d2cbe0f1249c228 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 14:59:34 -0600 Subject: [PATCH 13/22] more gpLoop' progress --- src/HushGP/GP.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index f805a80..5f74f19 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -33,7 +33,7 @@ gpLoop pushArgs = do unEvaledPopulation <- generatePopulation pushArgs -- let evaledPop = evaluatePopulation pushArgs unEvaledPopulation -- print evaledPop - print "gamer" + print "placeholder for now" -- | The guts of the GP loop. Where the work gets done after the initialization happens -- in the main gpLoop function. The first Int holds the generation count. The second Int @@ -58,7 +58,7 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do print "Incomplete Run, saving the best so far." | otherwise = gpLoop' pushArgs (succ generation) (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) - (if elitism then ) + (if elitism then bestInd : ) nextAction where -- \| This will have downsampling added to it later. From 70e4fa6ab674234a4821c7267db2b63570753d85 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 15:03:53 -0600 Subject: [PATCH 14/22] try with NoCC to fix tmux artifacting --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index e6bb793..3b713fc 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,7 @@ haskell-flake.url = "github:srid/haskell-flake"; }; outputs = inputs@{ self, nixpkgs, flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { + flake-parts.lib.mkFlakeNoCC { inherit inputs; } { systems = nixpkgs.lib.systems.flakeExposed; imports = [ inputs.haskell-flake.flakeModule ]; From 5a070bf2951e9d2803bbd72ee5bed9d4ba2b3b1e Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 15:10:39 -0600 Subject: [PATCH 15/22] attempt to add a shellHook --- flake.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flake.nix b/flake.nix index 3b713fc..c22571c 100644 --- a/flake.nix +++ b/flake.nix @@ -56,6 +56,10 @@ # Check that haskell-language-server works # hlsCheck.enable = true; # Requires sandbox to be disabled + + shellHook = '' + echo "Entering HushGP environment" + ''; }; }; From 4f888f44ae461aae55ed43ab065aa78aaad04240 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 15:20:53 -0600 Subject: [PATCH 16/22] mkFlakeNoCC doesn't exist lol --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index c22571c..6271bb0 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,7 @@ haskell-flake.url = "github:srid/haskell-flake"; }; outputs = inputs@{ self, nixpkgs, flake-parts, ... }: - flake-parts.lib.mkFlakeNoCC { inherit inputs; } { + flake-parts.lib.mkFlake { inherit inputs; } { systems = nixpkgs.lib.systems.flakeExposed; imports = [ inputs.haskell-flake.flakeModule ]; From 53c490b3b3450c51fa706c8f20b6584b47ae5d27 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 15:23:01 -0600 Subject: [PATCH 17/22] mission failed --- flake.nix | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flake.nix b/flake.nix index 6271bb0..e6bb793 100644 --- a/flake.nix +++ b/flake.nix @@ -56,10 +56,6 @@ # Check that haskell-language-server works # hlsCheck.enable = true; # Requires sandbox to be disabled - - shellHook = '' - echo "Entering HushGP environment" - ''; }; }; From 2f6675e9f5ebb5421ee841754d746d5cf7b555d6 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 18:47:50 -0600 Subject: [PATCH 18/22] add downsample --- HushGP.cabal | 1 + src/HushGP/GP/Downsample.hs | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 src/HushGP/GP/Downsample.hs diff --git a/HushGP.cabal b/HushGP.cabal index d0eb20d..3c95d40 100644 --- a/HushGP.cabal +++ b/HushGP.cabal @@ -63,6 +63,7 @@ library , HushGP.GP , HushGP.GP.PushArgs , HushGP.GP.Variation + , HushGP.GP.Downsample , HushGP.Problems.IntegerRegression diff --git a/src/HushGP/GP/Downsample.hs b/src/HushGP/GP/Downsample.hs new file mode 100644 index 0000000..06dc43e --- /dev/null +++ b/src/HushGP/GP/Downsample.hs @@ -0,0 +1,7 @@ +module HushGP.GP.Downsample where + +import HushGP.State +import HushGP.Genome + +updateCaseDistances :: [Individual] -> ([[Gene]], [Gene]) -> ([[Gene]], [Gene]) -> String -> Double -> ([[Gene]], [Gene]) +updateCaseDistances evaledPop downsampleData trainingData informedDownsamplingType solutionThreshold = undefined From 5012cb2ce152661cb50422680d266cbba20ccbe8 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 18:48:05 -0600 Subject: [PATCH 19/22] GP skeleton almost done --- src/HushGP/GP.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index 5f74f19..3bc4853 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -7,6 +7,8 @@ import Data.List (sort, uncons) import HushGP.GP.PushArgs import HushGP.Genome import HushGP.State +import HushGP.GP.Variation +import HushGP.GP.Downsample -- import Debug.Trace (trace, traceStack) @@ -58,7 +60,8 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do print "Incomplete Run, saving the best so far." | otherwise = gpLoop' pushArgs (succ generation) (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) - (if elitism then bestInd : ) + (if elitism pushArgs then bestInd : replicate (populationSize epsilonPushArgs - 1) (newIndividual epsilonPushArgs evaledPop) else replicate (populationSize epsilonPushArgs) (newIndividual epsilonPushArgs evaledPop)) + (if enableDownsampling pushArgs then if (generation `mod` downsampleParentsGens pushArgs) == 0 then updateCaseDistances repEvaluatedPop indexedTrainingData indexedTrainingData (informedDownsamplingType pushArgs) (solutionErrorThreshold pushArgs / fromIntegral @Int @Double (length $ fst indexedTrainingData)) else indexedTrainingData else indexedTrainingData) nextAction where -- \| This will have downsampling added to it later. From 2d9840c51ba0ec6c0025e595be591db4067ef614 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 18:53:52 -0600 Subject: [PATCH 20/22] skeleton done --- src/HushGP/GP.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index 3bc4853..5879ee0 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -61,7 +61,7 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do | otherwise = gpLoop' pushArgs (succ generation) (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) (if elitism pushArgs then bestInd : replicate (populationSize epsilonPushArgs - 1) (newIndividual epsilonPushArgs evaledPop) else replicate (populationSize epsilonPushArgs) (newIndividual epsilonPushArgs evaledPop)) - (if enableDownsampling pushArgs then if (generation `mod` downsampleParentsGens pushArgs) == 0 then updateCaseDistances repEvaluatedPop indexedTrainingData indexedTrainingData (informedDownsamplingType pushArgs) (solutionErrorThreshold pushArgs / fromIntegral @Int @Double (length $ fst indexedTrainingData)) else indexedTrainingData else indexedTrainingData) + (if enableDownsampling pushArgs && ((generation `mod` downsampleParentsGens pushArgs) == 0) then updateCaseDistances repEvaluatedPop indexedTrainingData indexedTrainingData (informedDownsamplingType pushArgs) (solutionErrorThreshold pushArgs / fromIntegral @Int @Double (length $ fst indexedTrainingData)) else indexedTrainingData) nextAction where -- \| This will have downsampling added to it later. From db497a087c122345495c35b3ea55d3ddb0852630 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 22:20:52 -0600 Subject: [PATCH 21/22] proper indexing --- src/HushGP/GP.hs | 32 +++++++++++++----------- src/HushGP/GP/Downsample.hs | 4 ++- src/HushGP/GP/PushArgs.hs | 2 +- src/HushGP/Problems/IntegerRegression.hs | 7 +++--- src/HushGP/Utility.hs | 21 ++++++++++++++++ 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index 5879ee0..7cdd5d8 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -9,6 +9,7 @@ import HushGP.Genome import HushGP.State import HushGP.GP.Variation import HushGP.GP.Downsample +import HushGP.Utility -- import Debug.Trace (trace, traceStack) @@ -21,7 +22,7 @@ generatePopulation pushArgs = do -- | Evaluates a population of plushies with the error function passed in via PushArgs and sorts them. -- TODO: Need to make this runnable in parallel too. -evaluatePopulation :: PushArgs -> ([[Gene]], [Gene]) -> [Individual] -> [Individual] +evaluatePopulation :: PushArgs -> ([[Gene]], [Gene], [Int]) -> [Individual] -> [Individual] evaluatePopulation pushArgs passedTrainingData population = sort $ zipWith updateIndividual (map (errorFunction pushArgs pushArgs passedTrainingData . plushy) population) population -- | A helper function used in evaluatePopulation. Takes a [Double] as the error scores and an individual. @@ -29,19 +30,21 @@ evaluatePopulation pushArgs passedTrainingData population = sort $ zipWith updat updateIndividual :: [Double] -> Individual -> Individual updateIndividual errors ind = ind {totalFitness = Just (sum errors), fitnessCases = Just errors} --- | The start of the gp loop. TODO: Make this more accurate later. +-- | The start of the gp loop. Generates the population and then calls +-- gpLoop' with modifications to the variables if needed. gpLoop :: PushArgs -> IO () gpLoop pushArgs = do unEvaledPopulation <- generatePopulation pushArgs - -- let evaledPop = evaluatePopulation pushArgs unEvaledPopulation - -- print evaledPop - print "placeholder for now" + let indexedTrainingData = makeIndexedTrainingData (trainingData pushArgs) + gpLoop' pushArgs 0 0 unEvaledPopulation indexedTrainingData + -- print "do this later" + -- | The guts of the GP loop. Where the work gets done after the initialization happens -- in the main gpLoop function. The first Int holds the generation count. The second Int -- holds the evaluation count. The list of Individuals is the population. The last parameter is -- the training data (possibly downsampled). -gpLoop' :: PushArgs -> Int -> Int -> [Individual] -> ([[Gene]], [Gene]) -> IO () +gpLoop' :: PushArgs -> Int -> Int -> [Individual] -> ([[Gene]], [Gene], [Int]) -> IO () gpLoop' pushArgs generation evaluations population indexedTrainingData = do print "Put information about each generation here." when bestIndPassesDownsample $ print $ "Semi Success Generation: " <> show generation @@ -56,17 +59,18 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do print "Total test error simplified: " <> undefined -- Implement later print $ "Simplified plushy: " <> undefined -- show simplifiedPlushy print $ "Simplified program: " <> undefined -- show plushyToPush simplifiedPlushy - | (not (enableDownsampling epsilonPushArgs) && (generation >= maxGenerations epsilonPushArgs)) || (enableDownsampling epsilonPushArgs && (evaluations >= (maxGenerations epsilonPushArgs * length population * length (fst indexedTrainingData)))) = - print "Incomplete Run, saving the best so far." + | (not (enableDownsampling epsilonPushArgs) && (generation >= maxGenerations epsilonPushArgs)) || (enableDownsampling epsilonPushArgs && (evaluations >= (maxGenerations epsilonPushArgs * length population * length (tfst indexedTrainingData)))) = + print $ "Best individual: " <> show (plushy bestInd) | otherwise = gpLoop' pushArgs (succ generation) - (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) - (if elitism pushArgs then bestInd : replicate (populationSize epsilonPushArgs - 1) (newIndividual epsilonPushArgs evaledPop) else replicate (populationSize epsilonPushArgs) (newIndividual epsilonPushArgs evaledPop)) - (if enableDownsampling pushArgs && ((generation `mod` downsampleParentsGens pushArgs) == 0) then updateCaseDistances repEvaluatedPop indexedTrainingData indexedTrainingData (informedDownsamplingType pushArgs) (solutionErrorThreshold pushArgs / fromIntegral @Int @Double (length $ fst indexedTrainingData)) else indexedTrainingData) + (evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (tfst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (tfst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0)) + (if elitism pushArgs + then bestInd : replicate (populationSize epsilonPushArgs - 1) (newIndividual epsilonPushArgs evaledPop) + else replicate (populationSize epsilonPushArgs) (newIndividual epsilonPushArgs evaledPop)) + (if enableDownsampling pushArgs && ((generation `mod` downsampleParentsGens pushArgs) == 0) + then updateCaseDistances repEvaluatedPop indexedTrainingData indexedTrainingData (informedDownsamplingType pushArgs) (solutionErrorThreshold pushArgs / fromIntegral @Int @Double (length $ tfst indexedTrainingData)) + else indexedTrainingData) nextAction where - -- \| This will have downsampling added to it later. - loopTrainData :: ([[Gene]], [Gene]) - loopTrainData = indexedTrainingData -- \| This will have downsampling functionality added later. parentReps :: [Individual] parentReps = [] diff --git a/src/HushGP/GP/Downsample.hs b/src/HushGP/GP/Downsample.hs index 06dc43e..65960ed 100644 --- a/src/HushGP/GP/Downsample.hs +++ b/src/HushGP/GP/Downsample.hs @@ -3,5 +3,7 @@ module HushGP.GP.Downsample where import HushGP.State import HushGP.Genome -updateCaseDistances :: [Individual] -> ([[Gene]], [Gene]) -> ([[Gene]], [Gene]) -> String -> Double -> ([[Gene]], [Gene]) +updateCaseDistances :: [Individual] -> ([[Gene]], [Gene], [Int]) -> ([[Gene]], [Gene], [Int]) -> String -> Double -> ([[Gene]], [Gene], [Int]) updateCaseDistances evaledPop downsampleData trainingData informedDownsamplingType solutionThreshold = undefined + +-- assignIndiciesToData :: diff --git a/src/HushGP/GP/PushArgs.hs b/src/HushGP/GP/PushArgs.hs index 9f0eddb..d108041 100644 --- a/src/HushGP/GP/PushArgs.hs +++ b/src/HushGP/GP/PushArgs.hs @@ -49,7 +49,7 @@ data PushArgs = PushArgs -- Arg 2: ([[Gene]], [Gene]) is the input data. Input is the first index and output is the second index. -- Arg 3: [Gene] is the plushy representation of a program. -- Returns the error list for a given set of inputs of type [Double]. - errorFunction :: PushArgs -> ([[Gene]], [Gene]) -> [Gene] -> [Double], + errorFunction :: PushArgs -> ([[Gene]], [Gene], [Int]) -> [Gene] -> [Double], -- | Type of informed downsampling. "solved", "elite", "soft". informedDownsamplingType :: String, -- | List of instructions to use in the evolutionary run. diff --git a/src/HushGP/Problems/IntegerRegression.hs b/src/HushGP/Problems/IntegerRegression.hs index 7abe7c2..2e88c13 100644 --- a/src/HushGP/Problems/IntegerRegression.hs +++ b/src/HushGP/Problems/IntegerRegression.hs @@ -57,8 +57,8 @@ loadState plushy vals = (loadProgram (plushyToPush plushy) emptyState){_input = Map.fromList (zip [0..] vals)} -- | The error function for a single set of inputs and outputs. -intErrorFunction :: PushArgs -> ([[Gene]], [Gene]) -> [Gene] -> [Double] -intErrorFunction _args (inputData, outputData) plushy = +intErrorFunction :: PushArgs -> ([[Gene]], [Gene], [Int]) -> [Gene] -> [Double] +intErrorFunction _args (inputData, outputData, _) plushy = map abs $ zipWith (-) (map ((fromIntegral @Integer @Double . (errorHead . _int) . interpretExec) . loadState plushy) inputData) (map (fromIntegral @Integer @Double . extractGeneInt) outputData) intPushArgs :: PushArgs @@ -76,7 +76,8 @@ intPushArgs = defaultPushArgs tournamentSize = 5, umadRate = 0.1, variation = Map.fromList [("umad", 1.0), ("crossover", 0.0)], - elitism = False + elitism = False, + enableDownsampling = False } main :: IO () diff --git a/src/HushGP/Utility.hs b/src/HushGP/Utility.hs index 384710c..3157f45 100644 --- a/src/HushGP/Utility.hs +++ b/src/HushGP/Utility.hs @@ -13,3 +13,24 @@ randomInstruction instructions = do -- | Generates a list of random instructions from a list of instructions passed in. randomInstructions :: Int -> [Gene] -> IO [Gene] randomInstructions amt instructions = replicateM amt (randomInstruction instructions) + +-- |Utility function: Used for indexed training data. Takes the first element of triple. +tfst :: (a, b, c) -> a +tfst (x, _, _) = x + +-- |Utility function: Used for indexed training data. Takes the second element of triple. +tsnd :: (a, b, c) -> b +tsnd (_, x, _) = x + +-- |Utility function: Used for indexed training data. Takes the third element of triple. +-- The third element in the context of indexed training data represents the index assigned. +thrd :: (a, b, c) -> c +thrd (_, _, x) = x + +-- |Utility function: Converts a tuple to a triple with a passed value. +tupleToTriple :: (a, b) -> c -> (a, b, c) +tupleToTriple (x, y) z = (x, y, z) + +-- |Utility function: Converts the training data passed in to an indexed representation +makeIndexedTrainingData :: ([[Gene]], [Gene]) -> ([[Gene]], [Gene], [Int]) +makeIndexedTrainingData (inputs, outputs) = (inputs, outputs, [0..(length inputs)]) From 5383356791286fd127346b66831e8c83c73877fa Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 25 Feb 2025 23:32:34 -0600 Subject: [PATCH 22/22] fix count discrepancy, thx Dr. Spector :) --- src/HushGP/GP.hs | 1 - src/HushGP/Instructions/Utility.hs | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/HushGP/GP.hs b/src/HushGP/GP.hs index 7cdd5d8..4a5a9ee 100644 --- a/src/HushGP/GP.hs +++ b/src/HushGP/GP.hs @@ -85,4 +85,3 @@ gpLoop' pushArgs generation evaluations population indexedTrainingData = do bestIndPassesDownsample = False -- TODO: fix this later epsilonPushArgs :: PushArgs epsilonPushArgs = pushArgs {epsilons = Nothing} -- TODO: And this ---gpLoop' _ _ _ _ _ = error "How did this happen?" diff --git a/src/HushGP/Instructions/Utility.hs b/src/HushGP/Instructions/Utility.hs index 1ab6f61..6ddd11b 100644 --- a/src/HushGP/Instructions/Utility.hs +++ b/src/HushGP/Instructions/Utility.hs @@ -171,7 +171,13 @@ findContainer _ _ = Block [] -- |Utility Function: A helper function for instructionCodeDiscrepancy. The full description is there. countDiscrepancy :: Gene -> Gene -> Integer -countDiscrepancy (Block xs) (Block ys) = sum [if uncurry (==) tup then 0 else 1 | tup <- zip xs ys] + abs (toInteger (length xs) - toInteger (length ys)) +-- countDiscrepancy (Block xs) (Block ys) = sum [if uncurry (==) tup then 0 else 1 | tup <- zip xs ys] + abs (toInteger (length xs) - toInteger (length ys)) +-- countDiscrepancy (Block xs) (Block ys) = sum [if isBlock (fst tup) && isBlock (snd tup) then uncurry countDiscrepancy tup else if uncurry (==) tup then 0 else 1 | tup <- zip xs ys] + abs (toInteger (length xs) - toInteger (length ys)) +countDiscrepancy (Block xs) (Block []) = codeRecursiveSize (Block xs) +countDiscrepancy (Block []) (Block ys) = codeRecursiveSize (Block ys) +countDiscrepancy (Block (x:xs)) (Block (y:ys)) = if x == y then 1 + countDiscrepancy (Block xs) (Block ys) else countDiscrepancy (Block xs) (Block ys) +countDiscrepancy _ (Block ys) = 1 + codeRecursiveSize (Block ys) +countDiscrepancy (Block xs) _ = 1 + codeRecursiveSize (Block xs) countDiscrepancy xgene ygene = if xgene == ygene then 1 else 0 -- |Utility Function: Extracts the first gene from a block. Returns itself if not a block