Skip to content

Commit 9f69e17

Browse files
authored
Sync dev with main (#434)
## Description This PR synchronizes `main` with `dev`, introducing a major refactor for PeTTa interpreter compatibility and structural alignment with OpenCog Classic MOSES. ### Key Highlights - **Performance**: Achieved a ~10x speedup by removing redundant `eval` calls, implementing caching for `scoreTree` and `getCscore`, and migrating to the PeTTa interpreter. - **OpenCog Classic Alignment**: Aligned `BuildLogical` complexity, tree reconstruction (`getCandidateRec`), and reduction rules (`0subsume`) with classic C++ MOSES behavior. - **Game Policy Domain**: Extended the framework to support game strategy evolution (e.g., Tic-Tac-Toe) via a new Strategy Subtree Knob (SSK) system and `PRIORITIZED-OR` operators. - **Baseline Players**: Added Minimax and Alpha-Beta Pruned Minimax players with immediate win detection. - **CI/CD**: Established a Dockerized testing environment to ensure stable, persistent validation with PeTTa. ## Motivation and Context These updates are essential for achieving architectural parity with OpenCog Classic, resolving performance bottlenecks for larger datasets (like parity-4), and enabling MOSES to evolve complex game strategies. ## How Has This Been Tested? - **Performance Benchmarks**: Verified execution time reduction from ~20 mins to ~2 mins on core tasks. - **Functional Tests**: Validated both Boolean and Game domain workflows using the new automated CI suite. - **Domain Validation**: Confirmed correct strategy evolution for Tic-Tac-Toe and parity/multiplexer results. ## Types of changes - [x] Bug fix - [x] New feature - [x] Breaking change (due to PeTTa migration and structural refactors) ## Checklist: - [x] Code follows project style. - [x] Added tests for new features. - [x] All CI tests passed.
2 parents 0907ce7 + 29faf1e commit 9f69e17

170 files changed

Lines changed: 8237 additions & 6240 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/MeTTa-CI.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
1-
name: MeTTaLog CI
1+
name: PeTTa CI
22

33
on:
44
push:
55
branches:
66
- main
77
- dev
8+
- feat/*
9+
- fix/*
810
- ref/*
9-
- reduct-refactoring
1011
pull_request:
1112
branches:
1213
- main
1314
- dev
15+
- feat/*
16+
- fix/*
1417
- ref/*
15-
- reduct-refactoring
1618

1719
jobs:
1820
setup-and-test:
1921
runs-on: ubuntu-latest
2022
container:
21-
image: indominyus/mettalog:latest
22-
23+
image: bluesky21/petta:v1.0.0
24+
2325
steps:
2426
- name: Checkout code
2527
uses: actions/checkout@v4
2628

27-
- name: Run mettalog test
29+
- name: Run PeTTa test
2830
run: |
29-
pip3 install ansi2html
30-
python3 scripts/run-tests.py
31+
python3 scripts/run-tests.py

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
GEMINI.md
12
.vscode/
23
venv
34
.venv
45
__pycache__/
6+
logs/
7+
.DS_Store
58

69
myTest.metta
710
todo
@@ -10,4 +13,4 @@ run_python_tests.py
1013

1114
# Ignore HTML and Prolog files
1215
*.html
13-
*.pl
16+
*.pl

deme/create-deme.metta

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
;; Represents a population of program instances with the same representation
22
;; but different knob settings, along with their scores
3-
(: Deme Type)
4-
(: mkDeme (-> Representation (InstanceSet $score) DemeId Deme))
3+
; (: Deme Type)
4+
; (: mkDeme (-> Representation (InstanceSet $score) DemeId Deme))
55
;; createDeme create demes with empty instance set -- appends deme ID's to them as well
66
;; $demeIds -- list of deme ids (Cons ...)
77
;; $exemplar -- the exemplar tree which the demes are going to be based on
@@ -10,18 +10,51 @@
1010
;; $prune-exemplar -- bool shall we prune the exemplar or not?
1111
;; $enforced-features -- tuple of enorced features to be sample during feature selection, if any, in the form of arg selection-probabality pair
1212
;; returns ↠ tuple of demes
13-
(: createDeme (-> Number (List DemeId) (Tree $a) (ITable $a) Symbol Bool Expression Symbol Expression))
13+
; (: createDeme (-> Number (List DemeId) (Tree $a) (ITable $a) Symbol Bool Expression Symbol Expression))
1414
(= (createDeme $nDeme $demeIds $exemplar (mkITable $table $labels) $algo $prune-exemplar $enforced-fts $scorerType)
15-
(let* ((() (println! (creating representations ....)))
16-
($reps (createRepresentation $nDeme $exemplar (mkITable $table $labels) $algo $prune-exemplar $enforced-fts $scorerType))
17-
(() (println! (done making representations ...))))
18-
(loopCreateDeme $demeIds $reps ())))
15+
(let*
16+
(
17+
;; ($_ (println! "creating representations ...."))
18+
($reps (createRepresentation $nDeme $exemplar (mkITable $table $labels) $algo $prune-exemplar $enforced-fts $scorerType))
19+
;; ($_ (println! (done making representations ... $reps)))
20+
)
21+
(loopCreateDeme $demeIds $reps ())
22+
)
23+
)
24+
;; Game strategy createDeme - creates demes with empty instance sets for game strategies
25+
;; $nDeme -- number of demes to create
26+
;; $demeIds -- list of deme ids (Cons ...)
27+
;; $exemplar -- the exemplar tree which the demes are going to be based on
28+
;; returns ↠ tuple of demes
29+
; (: createDeme (-> Number (List DemeId) (Tree $a) Expression))
30+
(= (createDeme $nDeme $demeIds $exemplar)
31+
(let*
32+
(
33+
;; ($_ (println! "creating game representations ...."))
34+
($reps (createRepresentation $nDeme $exemplar))
35+
;; ($_ (println! ("done making representations ..." $reps)))
36+
)
37+
(loopCreateDeme $demeIds $reps ())
38+
)
39+
)
40+
1941

2042
(= (loopCreateDeme $demeIds $reps $acc)
21-
(if-decons-expr-custom $reps $h $t
22-
(let* (($demeId (List.head $demeIds))
23-
($deme (mkDeme $h (mkSInstSet Nil) $demeId))
24-
($new-acc (union-atom $acc ($deme))))
43+
(if (== $reps ())
44+
$acc
45+
(let* (
46+
($h (car-atom $reps))
47+
($t (cdr-atom $reps)))
48+
(let* (
49+
($demeId (List.head $demeIds))
50+
($deme (mkDeme $h (mkSInstSet Nil) $demeId))
51+
($new-acc (union-atom $acc ($deme))))
52+
(loopCreateDeme (List.tail $demeIds) $t $new-acc)))))
53+
;; previous version of loopCreateDeme using if-decons-expr-custom
54+
; (if-decons-expr-custom $reps $h $t
55+
; (let* (($demeId (List.head $demeIds))
56+
; ($deme (mkDeme $h (mkSInstSet Nil) $demeId))
57+
; ($new-acc (union-atom $acc ($deme))))
2558

26-
(loopCreateDeme (List.tail $demeIds) $t $new-acc))
27-
$acc))
59+
; (loopCreateDeme (List.tail $demeIds) $t $new-acc))
60+
; $acc))

deme/deme-id-creation.metta

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,29 @@
44
;; the number of expansion is set to zero (0) in moses_params.h in the moses_statistics struct
55

66
;; for number to string conversion
7-
! (bind! numStr (py-atom str))
8-
7+
(= (numStr $n) (py-call (str $n)))
98
;; DemeId Type
10-
(: DemeId Type)
11-
(: mkDemeId (-> String DemeId)) ;; this deviates from earlier type definition for the type constructor. It was defined like (: mkDemeId (-> Number DemeId)
9+
; (: DemeId Type)
10+
; (: mkDemeId (-> String DemeId)) ;; this deviates from earlier type definition for the type constructor. It was defined like (: mkDemeId (-> Number DemeId))
1211

1312
;; createDemeIDs -- creates demeIDs for deme expansion
1413
;; $nExpansion -- total number of deme expansions (generations) set to 0 -- a constant number or a generation demes
1514
;; $nDemes -- Number of feature sets to select out of feature selection and demes to spawn
16-
(: createDemeIds (-> Number Number (List DemeId)))
15+
; (: createDemeIds (-> Number Number (List DemeId)))
1716
(= (createDemeIds $nExpansion $nDeme)
1817
(if (> $nDeme 1)
1918
(demeIdList (+ $nExpansion 1) (- $nDeme 1) Nil)
2019
(chain (+ $nExpansion 1) $n
21-
(chain (numStr $n) $str
22-
(chain (mkDemeId $str) $demeId (Cons $demeId Nil))))))
20+
(chain (numStr $n) $str
21+
(chain (mkDemeId $str ) $demeId (Cons $demeId Nil)))))
22+
)
2323

24-
;; demeIdList -- recursive function for creating list of demeId's
24+
;; demeIdList -- recursive function for creating list of demeId's
2525
(= (demeIdList $nExpansion $nDeme $list)
2626
(if (< $nDeme 0)
2727
$list
28-
(let* ((($str1 $str2)( (format-args "{}" ($nExpansion)) (format-args "{}" ($nDeme))))
29-
($demeId (mkDemeId (format-args "{}.{}" ($str1 $str2))))
28+
(let* ((($str1 $str2)($nExpansion $nDeme))
29+
($demeId (mkDemeId ($str1 . $str2)))
3030
($newList (List.prepend $demeId $list)))
3131

32-
(demeIdList $nExpansion (- $nDeme 1) $newList))))
32+
(demeIdList $nExpansion (- $nDeme 1) $newList))))

deme/expand-deme.metta

Lines changed: 107 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
(= (createInitialInstance $dscMp)
2+
(let* (
3+
($knobs (MultiMap.values $dscMp))
4+
($defaults (List.map getKnobDefault $knobs))
5+
)
6+
$defaults))
7+
18
;; Recursively optimizes each deme in a list using the provided optimizer function.
29
;; Params:
310
;; (List Deme): The list of demes to optimize.
4-
;; $truthTableBScore: The scoring function or object.
11+
;; $context: The scoring context - either TruthTableBScore or tuple ($nGames $opponentPolicy)
512
;; $inst: The initial instance for optimization.
613
;; $optimize: The optimizer function to apply to each deme.
714
;; Returns: (List Deme): A new list where each deme has been optimized.
8-
(: optimizeDemes (-> (List Deme) (TruthTableBScore $a) Instance (-> Deme (TruthTableBScore $a) Instance $Scorer (Instance Deme $state)) $Scorer (List (Instance Deme $state))))
9-
(= (optimizeDemes $deme $truthTableBScore $inst $optimize ($scorer $FsScorer))
10-
($optimize $deme $truthTableBScore $inst ($scorer $FsScorer)))
15+
; (: optimizeDemes (-> (List Deme) $context Instance $optimize (Instance Deme $state)))
16+
;; Game strategy version - unpacks tuple with target score
17+
;; Strategy version - unpacks game parameters and calls hillClimbing with mkParams
18+
(= (optimizeDemes $deme ($nGames $opponentPolicy $complexityRatio $targetCScore) $inst $optimize)
19+
($optimize $deme $targetCScore $inst (mkParams applyStrategyBasedScore ($nGames $opponentPolicy $complexityRatio))))
20+
;; Boolean version - passes context as-is
21+
(= (optimizeDemes $deme $context $inst $optimize)
22+
($optimize $deme $context $inst))
23+
24+
;; Extracts default specification from a knob
25+
; (: getKnobDefault (-> $knob Number))
26+
(= (getKnobDefault (mkLSK (mkDiscKnob $knob $multip $curr (mkDiscSpec $def) $dis))) $def)
27+
(= (getKnobDefault (mkSSK (mkDiscKnob $knob $multip $curr (mkDiscSpec $def) $dis))) $def)
1128

1229
;; Runs one deme-creation and optimization step, then merges the optimized deme into the metapopulation.
1330
;; Params:
@@ -25,48 +42,110 @@
2542
;; $capCoef: Capacity coefficient for resizing.
2643
;; $genCount: Generation count for resizing.
2744
;; Returns:
28-
;; $updatedMetaPop: Updated metapopulation with the new optimized deme merged in.
29-
(: expandDeme (-> (OS (Exemplar $a)) Number Number Bool Symbol (TruthTableBScore $a) (-> Deme (TruthTableBScore $a) Instance $Scorer (Instance Deme $state)) $Scorer Number Number Number Number (ITable $a) Number Number Number (OS (Exemplar $a))))
30-
(= (expandDeme $metaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $optimScorer $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $nToKeep $capCoef $genCount)
31-
(collapse
32-
(let*
33-
(
34-
($exemplar (trace! "Selecting Exemplar" (selectExemplar $metaPop)))
35-
(() (println! $exemplar))
45+
;; $updatedMetaPop: Updated metapopulation with the new optimized deme merged in.
46+
; (: expandDeme (-> (OS (Exemplar $a)) Number Number Bool Symbol (TruthTableBScore $a) (-> Deme (TruthTableBScore $a) Instance $Scorer (Instance Deme $state)) $Scorer Number Number Number Number (ITable $a) Number Number Number (OS (Exemplar $a))))
47+
(= (expandDeme $metaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $itable $scorerType)
48+
(let*
49+
(
50+
;; ($_ (println! ""))
51+
;; ($_ (println! (MetaPop: $metaPop)))
52+
;; ($_ (println! "Selecting Exemplar"))
53+
($exemplar (selectExemplar $metaPop))
54+
;; ($_ (println! $exemplar))
55+
;; ($_ (println! "getting exemplar tree"))
3656
($tree (getExemplarTree $exemplar))
57+
($_ (println! (SelectedTree: (preOrder $tree))))
58+
;; ($_ (println! "creating deme IDs"))
3759
($demeIds (createDemeIds $nExpansion $nDeme))
60+
;; ($_ (println! $demeIds))
3861
; ($argLabels (getArgLabels $itable))
39-
($demes (trace! "Creating Deme" (createDeme $nDeme $demeIds $tree $itable $fs-algo $prune-exemplar () $scorerType))) ;;createRepresentation and createDemeId are under this
40-
((mkDeme (mkRep (mkKbMap (mkDscKbMp $dscKbMp) (mkDscMp $dscMp)) $updatedTree) $sInstList $demeId) (superpose $demes))
62+
;; ($_ (println! "Creating Deme"))
63+
($demes (createDeme $nDeme $demeIds $tree $itable $fs-algo $prune-exemplar () $scorerType))
64+
)
65+
(map-atom $demes $deme (expandDemeHelper $deme $truthTableBScore $optimize))
66+
))
67+
68+
;; Overloaded version for game strategy domain - 8 parameters
69+
(= (expandDeme $metaPop $nExpansion $nDeme $nGames $opponentPolicy $complexityRatio $targetCScore $optimize)
70+
(let*
71+
(
72+
($exemplar (trace! "Selecting Exemplar" (selectExemplar $metaPop)))
73+
($tree (trace! "getting exemplar tree" (getExemplarTree $exemplar)))
74+
($demeIds (trace! "creating deme IDs" (createDemeIds $nExpansion $nDeme)))
75+
($demes (trace! "Creating Deme" (createDeme $nDeme $demeIds $tree)))
76+
($gameContext ($nGames $opponentPolicy $complexityRatio $targetCScore))
77+
)
78+
(map-atom $demes $deme (expandDemeHelper $deme $gameContext $optimize))))
79+
80+
;; Unified helper for both Boolean and Strategy domains
81+
;; Optimizes each deme in the list using the provided optimizer and context
82+
;; Params:
83+
;; $deme: Deme to optimize
84+
;; $context: Scoring context (ITable for Boolean, tuple for Strategy)
85+
;; $optimize: Optimizer function
86+
;; Returns: Optimized deme with scored instances
87+
(= (expandDemeHelper (mkDeme (mkRep (mkKbMap (mkDscKbMp $dscKbMp) (mkDscMp $dscMp)) $updatedTree) $sInstList $demeId) $context $optimize)
88+
(let*
89+
(
4190
($lengthOfDscKbMp (Map.length $dscKbMp))
42-
($generatedList (List.generate $lengthOfDscKbMp 0))
91+
;; ($generatedList (List.generate $lengthOfDscKbMp 0))
92+
($generatedList (createInitialInstance $dscMp))
4393
($deme (mkDeme (mkRep (mkKbMap (mkDscKbMp $dscKbMp) (mkDscMp $dscMp)) $updatedTree) $sInstList $demeId))
44-
(($inst $optimDeme $state) (trace! "Optimizing Deme" (optimizeDemes $deme $truthTableBScore (mkInst $generatedList) $optimize $optimScorer))) ;; the hillclimbing is called under this and it calls the neighborhood and stuff to first populate the deme with instances
94+
;; ($_ (println! (Rep: (mkRep (mkKbMap (mkDscKbMp $dscKbMp) (mkDscMp $dscMp)) $updatedTree))))
95+
;; ($_ (println! "Optimizing Deme"))
96+
(($inst $optimDeme $state) (optimizeDemes $deme $context (mkInst $generatedList) $optimize))
4597
)
46-
$optimDeme)))
47-
98+
$optimDeme
99+
))
48100
;; The main loop
49101
;; Takes 2 termination criterias:
50102
;; - $maxGen : how many times we want the loop to run
51103
;; - $maxScore : the maximum score we want the top Examplar to have
52104
;; It also takes:
53105
;; - $maxCandOutput : The number of candidates to be returned
54106
;; - other parameters required by the expandDeme method
55-
(: runMoses (-> Number Cscore Number (OS (Exemplar $a)) Number Number Bool Symbol (TruthTableBScore $a) (-> Deme (TruthTableBScore $a) Instance $Scorer (Instance Deme $state)) $Scorer Number Number Number Number (ITable $a) Number Number Number (OS (Exemplar $a))))
56-
(= (runMoses $maxGen $maxScore $maxCandOutput $metaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $optimScorer $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $nToKeep $capCoef $genCount)
107+
; (: runMoses (-> Number Cscore Number (OS (Exemplar $a)) Number Number Bool Symbol (TruthTableBScore $a) (-> Deme (TruthTableBScore $a) Instance $Scorer (Instance Deme $state)) $Scorer Number Number Number Number (ITable $a) Number Number Number (OS (Exemplar $a))))
108+
;; TODO: $nEval isn't being used. Increase it so that it works as expected.
109+
;; To enable that, we may need to change our termination condition so that the generation is equivalent to the number of expansion as well.
110+
;; The number of expansions in the open cog classic is equivalent to the number of deme expansions. That looks like the same thing we are doing here but not sure.
111+
(= (runMoses $maxGen $maxScore $maxCandOutput $metaPop $nExpansion $nDeme $context $optimize $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $nToKeep $capCoef $genCount)
57112
(if (== $maxGen 0)
58113
(if (<= $maxCandOutput (OS.length $metaPop))
59114
(let $topCandidates (OS.getTopN $maxCandOutput $metaPop) $topCandidates)
60115
$metaPop
61116
)
62-
; (trace! ($maxCandOutput $metaPop) (let $topCandidates (OS.getTopN $maxCandOutput $metaPop) $topCandidates))
63-
(let*
117+
(let*
64118
(
65-
($optimizedDemes (expandDeme $metaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $optimScorer $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $nToKeep $capCoef $genCount))
66-
($updatedMetaPop (trace! "Merging Deme" (mergeDemes $optimizedDemes $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $metaPop $nToKeep $capCoef $genCount)))
119+
($_ (println! (Generation $maxGen)))
120+
($optimizedDemes (case $context
121+
((($prune-exemplar $fs-algo $truthTableBScore $itable) (trace! "expand deme returnn" (expandDeme $metaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $itable mi)))
122+
(($nGames $opponentPolicy $complexityRatio) (trace! "expand deme return" (expandDeme $metaPop $nExpansion $nDeme $nGames $opponentPolicy $complexityRatio $maxScore $optimize))))))
123+
(((mkDeme $rep $sInst $demeId)) $optimizedDemes)
124+
($_ (println! (OptimizedDeme: (List.length (second $sInst)))))
125+
($updatedMetaPop (case $context
126+
((($prune-exemplar $fs-algo $truthTableBScore $itable) (trace! "Merging Deme" (mergeDemes $optimizedDemes $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $metaPop $nToKeep $capCoef $genCount)))
127+
(($nGames $opponentPolicy $complexityRatio) (trace! "Merging Deme" (mergeDemes $optimizedDemes $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature ($nGames $opponentPolicy) $metaPop $nToKeep $capCoef $genCount))))))
128+
;; ($_ (println! (Updated MetaPop: $updatedMetaPop)))
67129
($top (OS.getByIdx 0 $metaPop))
130+
;; ($_ (println! (Top Exemplar before expansion: $top)))
68131
($topScore (getExemplarCscore $top))
69-
($topCandidates (OS.getTopN $maxCandOutput $metaPop)))
70-
(if (cScore>= $topScore $maxScore)
71-
$topCandidates
72-
(runMoses (- $maxGen 1) $maxScore $maxCandOutput $updatedMetaPop $nExpansion $nDeme $prune-exemplar $fs-algo $truthTableBScore $optimize $optimScorer $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $itable $nToKeep $capCoef $genCount)))))
132+
;; ($_ (println! (topscore: $topScore)))
133+
($resultCandi (if (<= $maxCandOutput (OS.length $updatedMetaPop)) (OS.getTopN $maxCandOutput $updatedMetaPop) $updatedMetaPop))
134+
;; ($_ (println! (Top: $topScore Max: $maxScore (cScore>= $topScore $maxScore))))
135+
($_ (cut))
136+
($_ (println! ""))
137+
)
138+
(if (== $topScore $maxScore)
139+
$resultCandi
140+
(runMoses (- $maxGen 1) $maxScore $maxCandOutput $updatedMetaPop $nExpansion $nDeme $context $optimize $nEval $maxCandsPerDeme $minPoolSize $complexityTemperature $nToKeep $capCoef $genCount)))))
141+
142+
143+
;; Some helpers specific to final testing
144+
(= (mapG $f NilOS) NilOS)
145+
(= (mapG $f Nil) Nil)
146+
(= (mapG $f ($ctor $x $xs)) ($ctor ($f $x) (mapG $f $xs)))
147+
148+
(= (printExemplar (mkExemplar $tree $demeId $cscore $bscore)) (println! (Tree: (preOrder $tree) CScore: $cscore Bscore: $bscore)))
149+
(= (cleanedExemplar (mkExemplar $tree $demeId $cscore (mkBScore $bscore))) ((preOrder $tree) (List.sum $bscore)))
150+
151+
(= (printInstance (mkInst $inst)) (println! (List.listToExpr $inst)))

0 commit comments

Comments
 (0)