OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-agents commits / 1b62506

Start working on a debuggab le demo
1b62506d5c0560947bac447342c207a04d745a3f
by Linas Vepštas <linasvepstas@gmail.com> · 2024-05-02T18:37:59.000Z

Changed files (2) +197 -64

examples/pair-count.scm +193 -0
……@@ -0,0 +1,193 @@
1;
2; pair-count.scm -- Example demo of assembling a word-pair counter.
3;
4; Prototype hand-built agent capable of counting things observed
5; in the external environment.
6;
7; Demos counting of word-pairs observed by looking at a text file.
8; The Sensory API allows easy generalization to other text sources,
9; so working with files is OK for now.
10;
11; The dataflow pipeline is hand-crafted. (The sensory API is supposed
12; to eventually auto-build these pipelines, but that code is not working
13; yet.) You'll notice that the pipeline below is ... complicated.
14;
15; The "rest of the demo", where we do something with the word pairs,
16; is in "generate.scm".
17
18(use-modules (opencog) (opencog exec) (opencog persist))
19(use-modules (opencog nlp) (opencog nlp lg-parse))
20(use-modules (opencog sensory))
21(use-modules (srfi srfi-1))
22
23; --------------------------------------------------------------
24; Return a text parser that counts edges on a stream. The
25; `txt-stream` must be an Atom that can serve as a source of text.
26; Typically, `txt-stream` will be
27; (ValueOf (Concept "some atom") (Predicate "some key"))
28; and the Value there will be a LinkStream from some file or
29; other text source.
30;
31; These sets up a processing pipeline in Atomese, and returns that
32; pipeline. The actual parsing all happens in C++ code, not in scheme
33; code. The scheme here is just to glue the pipeline together.
34(define (make-parser txt-stream)
35 ;
36 ; Pipeline steps, from inside to out:
37 ; * LGParseBonds tokenizes a sentence, and then parses it.
38 ; * The PureExecLink makes sure that the parsing is done in a
39 ; sub-AtomSpace so that the main AtomSpace is not garbaged up.
40 ;
41 ; The result of parsing is a list of pairs. First item in a pair is
42 ; the list of words in the sentence; the second is a list of the edges.
43 ; Thus, each pair has the form
44 ; (LinkValue
45 ; (LinkValue (Word "this") (Word "is") (Word "a") (Word "test"))
46 ; (LinkValue (Edge ...) (Edge ...) ...))
47 ;
48 ; The outer Filter matches this, so that (Glob "$edge-list") is
49 ; set to the LinkValue of Edges.
50 ;
51 ; The inner Filter loops over the list of edges, and invokes a small
52 ; pipe to increment the count on each edge.
53 ;
54 ; The counter is a non-atomic pipe of (SetValue (Plus 1 (GetValue)))
55 ;
56 (define NUML (Number 4))
57 (define DICT (LgDict "any"))
58
59 ; Increment the count on one edge.
60 (define (incr-cnt edge)
61 (SetValue edge (Predicate "count")
62 (Plus (Number 0 0 1)
63 (FloatValueOf edge (Predicate "count") (FloatValueOf (Number 0 0 0))))))
64
65 ; Given a list (an Atomese LinkValue list) of parse results,
66 ; extract the edges and increment the count on them.
67 (define (count-edges parsed-stuff)
68 (Filter
69 (Rule
70 ; (Variable "$edge")
71 (TypedVariable (Variable "$edge") (Type 'Edge))
72 (Variable "$edge")
73 (incr-cnt (Variable "$edge")))
74 parsed-stuff))
75
76 ; Parse text in a private space.
77 (define (parseli phrali)
78 (PureExecLink (LgParseBonds phrali DICT NUML)))
79
80 ; Given an Atom `phrali` holding text to be parsed, get that text,
81 ; parse it, and increment the counts on the edges.
82 (define (filty phrali)
83 (Filter
84 (Rule
85 ; Type decl
86 (Glob "$x")
87 ; Match clause - one per parse.
88 (LinkSignature
89 (Type 'LinkValue) ; the wrapper for the pair
90 (Type 'LinkValue) ; the word-list
91 (LinkSignature ; the edge-list
92 (Type 'LinkValue) ; edge-list wrapper
93 (Glob "$edge-list"))) ; all of the edges
94 ; Pipeline to apply to the resulting match.
95 (count-edges (Glob "$edge-list"))
96 )
97 (parseli phrali)))
98
99 ; Return the parser.
100 (filty txt-stream)
101)
102
103; --------------------------------------------------------------
104; Demo wrapper showing how to use the above: Parse a string.
105; PLAIN-TEXT should be a scheme string.
106;
107; Verify this works as follows:
108; (obs-txt "this is a test")
109; (cog-report-counts)
110; Note the abundance of WordNodes listed in the report.
111; (cog-get-atoms 'WordNode)
112; Cleanup after use:
113; (for-each cog-extract-recursive! (cog-get-atoms 'WordNode))
114; (extract-type 'WordNode)
115(define (obs-txt PLAIN-TEXT)
116
117 ; We don't need to create this over and over; once is enough.
118 (define txt-stream (ValueOf (Concept "foo") (Predicate "some place")))
119 (define parser (make-parser txt-stream))
120
121 (define phrali (Phrase PLAIN-TEXT))
122 (cog-set-value! (Concept "foo") (Predicate "some place") phrali)
123
124 (cog-execute! parser)
125
126 ; Remove the phrase-link, return the list of edges.
127 (cog-set-value! (Concept "foo") (Predicate "some place") #f)
128 (cog-extract-recursive! phrali)
129)
130
131; --------------------------------------------------------------
132; Demo wrapper: Parse contents of a file.
133(define (obs-file FILE-NAME)
134
135 ; We don't need to create this over and over; once is enough.
136 (define txt-stream (ValueOf (Concept "foo") (Predicate "some place")))
137 (define parser (make-parser txt-stream))
138
139 (define phrali (Open (Type 'TextFileStream)
140 (Sensory (string-append "file://" FILE-NAME))))
141
142 (cog-execute!
143 (SetValue (Concept "foo") (Predicate "some place") phrali))
144
145 ; Parse only first line of file:
146 ; (cog-execute! parser)
147
148 ; Loop over all lines.
149 (define (looper) (cog-execute! parser) (looper))
150
151 ; At end of file, exception will be thrown. Catch it.
152 (catch #t looper
153 (lambda (key . args) (format #t "The end ~A\n" key)))
154
155 (cog-extract-recursive! phrali)
156)
157
158; --------------------------
159; Issues.
160; Parses arrive as LinkValues. We want to apply a function to each.
161; How? Need:
162; 4) IncrementLink just like cog-inc-value!
163; or is it enough to do atomic lock?
164; Make SetValue exec under lock. ... easier said than done.
165; risks deadlock.
166;
167; cog-update-value calls
168; Ideally, call asp->increment_count(h, key, fvp->value()));
169
170; Below works but is a non-atomic increment.
171(define ed (Edge (Bond "ANY") (List (Word "words") (Word "know"))))
172(define (doinc)
173 (cog-execute!
174 (SetValue ed (Predicate "count")
175 (Plus (Number 0 0 1)
176 (FloatValueOf ed (Predicate "count") (Number 0 0 0))))))
177
178; Here's a demo of what file-access looks like.
179(cog-execute!
180 (SetValue (Concept "foo") (Predicate "some place")
181 (FileRead "file:///tmp/demo.txt")))
182
183; Wait ...
184(define txt-stream-gen
185 (ValueOf (Concept "foo") (Predicate "some place")))
186
187; (cog-execute! (LgParseBonds txt-stream-gen DICT NUML))
188
189; (load "count-agent.scm")
190; (obs-txt "Some kind of sentence to process, hell yeah!")
191; (obs-file "/tmp/demo.txt")
192; (cog-report-counts)
193; (cog-get-atoms 'WordNode)
scm/pair-agent/count-agent.scm +4 -64
……@@ -1,5 +1,5 @@
11;
2; count-agent.scm
2; count-agent.scm -- Agent that performs word-pair counting.
33;
44; Prototype hand-built agent capable of counting things observed
55; in the external environment.
……@@ -12,6 +12,9 @@
1212; to eventually auto-build these pipelines, but that code is not working
1313; yet.) You'll notice that the pipeline below is ... complicated.
1414;
15; Most of the demo is in the `examples/pair-count.scm` file. The below
16; is a condensation of that demo into it's working core.
17;
1518; The "rest of the demo", where we do something with the word pairs,
1619; is in "generate.scm".
1720
……@@ -100,34 +103,6 @@
100103 (filty txt-stream)
101104)
102105
103; --------------------------------------------------------------
104; Demo wrapper showing how to use the above: Parse a string.
105; PLAIN-TEXT should be a scheme string.
106;
107; Verify this works as follows:
108; (obs-txt "this is a test")
109; (cog-report-counts)
110; Note the abundance of WordNodes listed in the report.
111; (cog-get-atoms 'WordNode)
112; Cleanup after use:
113; (for-each cog-extract-recursive! (cog-get-atoms 'WordNode))
114; (extract-type 'WordNode)
115(define (obs-txt PLAIN-TEXT)
116
117 ; We don't need to create this over and over; once is enough.
118 (define txt-stream (ValueOf (Concept "foo") (Predicate "some place")))
119 (define parser (make-parser txt-stream))
120
121 (define phrali (Phrase PLAIN-TEXT))
122 (cog-set-value! (Concept "foo") (Predicate "some place") phrali)
123
124 (cog-execute! parser)
125
126 ; Remove the phrase-link, return the list of edges.
127 (cog-set-value! (Concept "foo") (Predicate "some place") #f)
128 (cog-extract-recursive! phrali)
129)
130
131106; --------------------------------------------------------------
132107; Demo wrapper: Parse contents of a file.
133108(define (obs-file FILE-NAME)
……@@ -156,38 +131,3 @@
156131)
157132
158133; --------------------------
159; Issues.
160; Parses arrive as LinkValues. We want to apply a function to each.
161; How? Need:
162; 4) IncrementLink just like cog-inc-value!
163; or is it enough to do atomic lock?
164; Make SetValue exec under lock. ... easier said than done.
165; risks deadlock.
166;
167; cog-update-value calls
168; Ideally, call asp->increment_count(h, key, fvp->value()));
169
170; Below works but is a non-atomic increment.
171(define ed (Edge (Bond "ANY") (List (Word "words") (Word "know"))))
172(define (doinc)
173 (cog-execute!
174 (SetValue ed (Predicate "count")
175 (Plus (Number 0 0 1)
176 (FloatValueOf ed (Predicate "count") (Number 0 0 0))))))
177
178; Here's a demo of what file-access looks like.
179(cog-execute!
180 (SetValue (Concept "foo") (Predicate "some place")
181 (FileRead "file:///tmp/demo.txt")))
182
183; Wait ...
184(define txt-stream-gen
185 (ValueOf (Concept "foo") (Predicate "some place")))
186
187; (cog-execute! (LgParseBonds txt-stream-gen DICT NUML))
188
189; (load "count-agent.scm")
190; (obs-txt "Some kind of sentence to process, hell yeah!")
191; (obs-file "/tmp/demo.txt")
192; (cog-report-counts)
193; (cog-get-atoms 'WordNode)