| … | … | @@ -6,6 +6,7 @@ |
| 6 | 6 | ; |
| 7 | 7 | (use-modules (opencog)) |
| 8 | 8 | (use-modules (opencog test-runner)) |
| 9 | (use-modules (srfi srfi-1)) ; for fold |
| 9 | 10 | |
| 10 | 11 | (opencog-test-runner) |
| 11 | 12 | (define tname "count-pipeline-test") |
| … | … | @@ -44,7 +45,7 @@ |
| 44 | 45 | |
| 45 | 46 | ; Run the counting pipeline for the first time. |
| 46 | 47 | ; At this point, relatively few atoms exist in the AtomSpace. |
| 47 | | (cog-execute! (Name "count-types")) |
| 48 | (Trigger (Name "count-types")) |
| 48 | 49 | |
| 49 | 50 | ; ------------------------------------------------------------ |
| 50 | 51 | ; Pipeline Stage 4: De-duplicate types |
| … | … | @@ -115,7 +116,7 @@ |
| 115 | 116 | ; Some TypeNodes created after count-types ran won't have counts. |
| 116 | 117 | |
| 117 | 118 | (reset-counters) |
| 118 | | (define first-result (cog-execute! guarded-counter)) |
| 119 | (define first-result (Trigger guarded-counter)) |
| 119 | 120 | |
| 120 | 121 | ; The result should be a LinkValue containing TypeNodes |
| 121 | 122 | (test-assert "first-result-is-link-value" |
| … | … | @@ -139,10 +140,10 @@ |
| 139 | 140 | ; TEST 3: Run count-types again - this will add to existing counts |
| 140 | 141 | ; and count new types that were created (like AndLink from the guard) |
| 141 | 142 | |
| 142 | | (cog-execute! (Name "count-types")) |
| 143 | (Trigger (Name "count-types")) |
| 143 | 144 | |
| 144 | 145 | (reset-counters) |
| 145 | | (cog-execute! guarded-counter) |
| 146 | (Trigger guarded-counter) |
| 146 | 147 | |
| 147 | 148 | (format #t "Second run: ~A types, total count ~A\n" |
| 148 | 149 | *type-count* *total-count*) |
| … | … | @@ -164,10 +165,10 @@ |
| 164 | 165 | ; ------------------------------------------------------------ |
| 165 | 166 | ; TEST 5: Run count-types a third time and verify continued accumulation |
| 166 | 167 | |
| 167 | | (cog-execute! (Name "count-types")) |
| 168 | (Trigger (Name "count-types")) |
| 168 | 169 | |
| 169 | 170 | (reset-counters) |
| 170 | | (cog-execute! guarded-counter) |
| 171 | (Trigger guarded-counter) |
| 171 | 172 | |
| 172 | 173 | (format #t "Third run: ~A types, total count ~A\n" |
| 173 | 174 | *type-count* *total-count*) |
| … | … | @@ -205,7 +206,7 @@ |
| 205 | 206 | (Number 0 0 1))) |
| 206 | 207 | (Name "unique-types"))) |
| 207 | 208 | |
| 208 | | (cog-execute! (Name "grand-total")) |
| 209 | (Trigger (Name "grand-total")) |
| 209 | 210 | |
| 210 | 211 | (define tot-types (cog-value-ref (Any "totals") (Predicate "tot-types") 2)) |
| 211 | 212 | (test-assert "tot-types" (= 27 tot-types)) |
| … | … | @@ -245,7 +246,7 @@ |
| 245 | 246 | ;;; what lies down that path. I have no desire to create a Sinclair |
| 246 | 247 | ;;; Z80 in Minecraft. We're not emulating to emulate badly and |
| 247 | 248 | ;;; inefficiently. ... at least not yet ... :-/ |
| 248 | | ;;; (cog-execute! (Name "deref grand-total")) |
| 249 | ;;; (Trigger (Name "deref grand-total")) |
| 249 | 250 | ;;; |
| 250 | 251 | ;;; (define tot-types (cog-value-ref (Any "deref totals") (Predicate "tot-types") 2)) |
| 251 | 252 | ;;; (test-assert "deref tot-types" (= 27 tot-types)) |
| … | … | @@ -253,6 +254,110 @@ |
| 253 | 254 | ;;; (define tot-counts (cog-value-ref (Any "deref totals") (Predicate "tot-counts") 2)) |
| 254 | 255 | ;;; (test-assert "deref tot-counts" (= 182 tot-counts)) |
| 255 | 256 | |
| 257 | ; ------------------------------------------------------------ |
| 258 | ; TEST 8: Verify PureExec prevents double-counting |
| 259 | ; |
| 260 | ; This tests the full type-counts pipeline from atomspace-viz. |
| 261 | ; The key feature is that it uses PureExec to run the counting in |
| 262 | ; a fresh (scratch) AtomSpace, which prevents double-counting. |
| 263 | ; Running the pipeline multiple times should produce identical results. |
| 264 | |
| 265 | ; Define the comparison predicate for sorting types by count. |
| 266 | (DefineLink |
| 267 | (DefinedPredicate "viz-count-order") |
| 268 | (Lambda |
| 269 | (VariableList (Variable "left") (Variable "right")) |
| 270 | (Not |
| 271 | (LessThan |
| 272 | (ElementOf (Number 2) |
| 273 | (ValueOf (Variable "left") (Predicate "vcnt"))) |
| 274 | (ElementOf (Number 2) |
| 275 | (ValueOf (Variable "right") (Predicate "vcnt"))))))) |
| 276 | |
| 277 | ; The type-counting pipeline that uses PureExec to prevent double-counting. |
| 278 | ; This is a copy of the pipeline from atomspace-viz/analytics/count-types.scm |
| 279 | (PipeLink |
| 280 | (Name "viz-type-counts") |
| 281 | (PureExec |
| 282 | (Filter |
| 283 | (Rule |
| 284 | (TypedVariable (Variable "$typ") (Type 'Type)) |
| 285 | ; Guard: only accept types that have a count attached |
| 286 | (And |
| 287 | (Present (Variable "$typ")) |
| 288 | (Equal |
| 289 | (Type 'FloatValue) |
| 290 | (TypeOf (ValueOf (Variable "$typ") (Predicate "vcnt"))))) |
| 291 | ; Output: table row with type and count |
| 292 | (LinkSignature (Type 'LinkValue) |
| 293 | (Variable "$typ") |
| 294 | (ValueOf (Variable "$typ") (Predicate "vcnt")))) |
| 295 | ; Input: sorted unique types |
| 296 | (LinkSignature |
| 297 | (TypeNode 'SortedValue) |
| 298 | (DefinedPredicate "viz-count-order") |
| 299 | ; Input: unique types (deduplicated) |
| 300 | (CollectionOf (TypeNode 'UnisetValue) |
| 301 | ; Input: types extracted from atoms, with counts attached |
| 302 | (Filter |
| 303 | (Rule |
| 304 | (TypedVariable (Variable "$typ") (Type 'Type)) |
| 305 | (Variable "$typ") |
| 306 | ; Increment the count on this TypeNode, return TypeNode |
| 307 | (IncrementValueOn (Variable "$typ") (Predicate "vcnt") (Number 0 0 1))) |
| 308 | ; Input: types of all atoms |
| 309 | (Filter |
| 310 | (Rule |
| 311 | (TypedVariable (Variable "$atom") (Type 'Atom)) |
| 312 | (Variable "$atom") |
| 313 | (TypeOf (DontExec (Variable "$atom")))) |
| 314 | ; Input: all atoms in the base AtomSpace |
| 315 | (PureExec |
| 316 | (AtomSpaceOf (Link)) |
| 317 | (Meet |
| 318 | (Variable "$atom") |
| 319 | (Variable "$atom")))))))))) |
| 320 | |
| 321 | ; Helper to sum all counts in the result |
| 322 | (define (sum-counts result) |
| 323 | (fold (lambda (row acc) |
| 324 | (+ acc (inexact->exact (cog-value-ref (cog-value-ref row 1) 2)))) |
| 325 | 0 |
| 326 | (cog-value->list result))) |
| 327 | |
| 328 | ; Helper to count rows in result |
| 329 | (define (count-rows result) |
| 330 | (length (cog-value->list result))) |
| 331 | |
| 332 | ; Run the pipeline three times and collect results |
| 333 | (define viz-result-1 (Trigger (Name "viz-type-counts"))) |
| 334 | (define viz-rows-1 (count-rows viz-result-1)) |
| 335 | (define viz-sum-1 (sum-counts viz-result-1)) |
| 336 | |
| 337 | (format #t "PureExec run 1: ~A types, total ~A\n" viz-rows-1 viz-sum-1) |
| 338 | |
| 339 | (define viz-result-2 (Trigger (Name "viz-type-counts"))) |
| 340 | (define viz-rows-2 (count-rows viz-result-2)) |
| 341 | (define viz-sum-2 (sum-counts viz-result-2)) |
| 342 | |
| 343 | (format #t "PureExec run 2: ~A types, total ~A\n" viz-rows-2 viz-sum-2) |
| 344 | |
| 345 | (define viz-result-3 (Trigger (Name "viz-type-counts"))) |
| 346 | (define viz-rows-3 (count-rows viz-result-3)) |
| 347 | (define viz-sum-3 (sum-counts viz-result-3)) |
| 348 | |
| 349 | (format #t "PureExec run 3: ~A types, total ~A\n" viz-rows-3 viz-sum-3) |
| 350 | |
| 351 | ; The key test: all three runs should produce identical counts |
| 352 | ; If double-counting occurs, the counts would increase each time |
| 353 | (test-assert "pureexec-consistent-row-count-1-2" (= viz-rows-1 viz-rows-2)) |
| 354 | (test-assert "pureexec-consistent-row-count-2-3" (= viz-rows-2 viz-rows-3)) |
| 355 | (test-assert "pureexec-consistent-total-1-2" (= viz-sum-1 viz-sum-2)) |
| 356 | (test-assert "pureexec-consistent-total-2-3" (= viz-sum-2 viz-sum-3)) |
| 357 | |
| 358 | (format #t "PureExec consistency verified: ~A types, ~A total (all 3 runs identical)\n" |
| 359 | viz-rows-1 viz-sum-1) |
| 360 | |
| 256 | 361 | ; ------------------------------------------------------------ |
| 257 | 362 | (test-end tname) |
| 258 | 363 | (opencog-test-end) |