| … | … | @@ -9,161 +9,76 @@ |
| 9 | 9 | ; to eventually auto-build these pipelines, but that code is not working |
| 10 | 10 | ; yet.) |
| 11 | 11 | ; |
| 12 | ; The hand-crafted pipe not only works, but works faster than the old |
| 13 | ; code, and so has been mad "official" and is now a part of the `learn` |
| 14 | ; module. See |
| 15 | ; https://github.com/opencog/learn/tree/master/sc,/pipe-parse/pipe-count.scm |
| 16 | ; |
| 12 | 17 | ; The "rest of the demo", where we do something with the word pairs, |
| 13 | 18 | ; is in "generate.scm". |
| 14 | 19 | |
| 15 | 20 | (use-modules (opencog) (opencog exec) (opencog persist)) |
| 16 | 21 | (use-modules (opencog nlp) (opencog nlp lg-parse)) |
| 22 | (use-modules (opencog learn)) |
| 17 | 23 | (use-modules (opencog sensory)) |
| 18 | 24 | (use-modules (srfi srfi-1)) |
| 19 | 25 | |
| 20 | 26 | ; -------------------------------------------------------------- |
| 21 | | ; Return a text parser that counts words and word-pairs obtained from |
| 22 | | ; parsing text on a stream. The `txt-stream` must be an Atom that can |
| 23 | | ; serve as a source of text. Typically, `txt-stream` will be |
| 24 | | ; (ValueOf (Concept "some atom") (Predicate "some key")) |
| 25 | | ; and the Value there will be a LinkStream from some file or |
| 26 | | ; other text source. |
| 27 | | ; |
| 28 | | ; These sets up a processing pipeline in Atomese, and returns that |
| 29 | | ; pipeline. The actual parsing all happens in C++ code, not in scheme |
| 30 | | ; code. The scheme here is just to glue the pipeline together. |
| 31 | | ; |
| 32 | | ; This is attempting to be backwards-compat with old pair-counting code. |
| 33 | | (define (make-parser txt-stream) |
| 34 | | ; |
| 35 | | ; Pipeline steps, from inside to out: |
| 36 | | ; * LGParseBonds tokenizes a sentence, and then parses it. |
| 37 | | ; * The PureExecLink makes sure that the parsing is done in a |
| 38 | | ; sub-AtomSpace so that the main AtomSpace is not garbaged up. |
| 39 | | ; |
| 40 | | ; The result of parsing is a list of pairs. First item in a pair is |
| 41 | | ; the list of words in the sentence; the second is a list of the edges. |
| 42 | | ; Thus, each pair has the form |
| 43 | | ; (LinkValue |
| 44 | | ; (LinkValue (Word "this") (Word "is") (Word "a") (Word "test")) |
| 45 | | ; (LinkValue (Edge ...) (Edge ...) ...)) |
| 46 | | ; |
| 47 | | ; The outer Filter matches this, so that (Glob "$edge-list") is |
| 48 | | ; set to the LinkValue of Edges. |
| 49 | | ; |
| 50 | | ; The inner Filter loops over the list of edges, and invokes a small |
| 51 | | ; pipe to increment the count on each edge. |
| 52 | | ; |
| 53 | | ; The counter is a non-atomic pipe of (SetValue (Plus 1 (GetValue))) |
| 54 | | ; |
| 55 | | (define NUML (Number 6)) |
| 56 | | (define DICT (LgDict "any")) |
| 57 | | (define any-parse (ParseNode "ANY")) |
| 58 | | |
| 59 | | ; Increment the count on one atom. |
| 60 | | (define (incr-cnt atom) |
| 61 | | (SetValue atom (Predicate "count") |
| 62 | | (Plus (Number 0 0 1) |
| 63 | | (FloatValueOf atom (Predicate "count") |
| 64 | | (FloatValueOf (Number 0 0 0)))))) |
| 65 | | |
| 66 | | ; Given a list (an Atomese LinkValue list) of Atoms, |
| 67 | | ; increment the count on each Atom. |
| 68 | | (define (atom-counter ATOM-LIST) |
| 69 | | (Filter |
| 70 | | (Rule |
| 71 | | ; We could type for safety, but seems like no need... |
| 72 | | ; (TypedVariable (Variable "$atom") |
| 73 | | ; (TypeChoice (Type 'Edge) (Type 'Word))) |
| 74 | | (Variable "$atom") ; vardecl |
| 75 | | (Variable "$atom") ; body to match |
| 76 | | (incr-cnt (Variable "$atom"))) |
| 77 | | ATOM-LIST)) |
| 78 | | |
| 79 | | ; Given PASRC holding a stream of parses, split it into a list of |
| 80 | | ; words, and a list of edges, and apply FUNKY to both lists. |
| 81 | | (define (stream-splitter PASRC FUNKY) |
| 82 | | (Filter |
| 83 | | (Rule |
| 84 | | (LinkSignature |
| 85 | | (Type 'LinkValue) |
| 86 | | (Variable "$word-list") |
| 87 | | (Variable "$edge-list")) |
| 88 | | ; Apply the function FUNKY to the word and edge lists. |
| 89 | | (FUNKY (Variable "$word-list")) |
| 90 | | (FUNKY (Variable "$edge-list")) |
| 91 | | ; Increment by one for each parse |
| 92 | | (incr-cnt any-parse)) |
| 93 | | PASRC)) |
| 94 | | |
| 95 | | (define parser (LgParseBonds txt-stream DICT NUML)) |
| 96 | | |
| 97 | | ; Is there any benefit to private parsing? |
| 98 | | ; (edge-filter (PureExecLink parser) edge-counter) |
| 99 | | |
| 100 | | ; Return the assembled counting pipeline. |
| 101 | | ; All that the user needs to do is to call `cog-execute!` on it, |
| 102 | | ; until end of file is reached. |
| 103 | | (stream-splitter parser atom-counter) |
| 27 | (define (setup-parser STORAGE) |
| 28 | " |
| 29 | Create parser attached to storage. |
| 30 | " |
| 31 | ; (make-disjunct-parser ... |
| 32 | (make-random-pair-parser |
| 33 | (ValueOf (Anchor "parse pipe") (Predicate "text src")) |
| 34 | STORAGE) |
| 104 | 35 | ) |
| 105 | 36 | |
| 106 | | ; -------------------------------------------------------------- |
| 107 | | ; Demo wrapper: Parse a single text string. |
| 108 | | (define (obs-texty TXT-STRING) |
| 109 | | |
| 110 | | ; We don't need to create this over and over; once is enough. |
| 111 | | (define txt-stream |
| 112 | | (ValueOf (Anchor "parse pipe") (Predicate "text src"))) |
| 113 | | (define parser (make-parser txt-stream)) |
| 114 | | |
| 115 | | (define phrali (Item TXT-STRING)) |
| 116 | | (cog-execute! |
| 117 | | (SetValue (Anchor "parse pipe") (Predicate "text src") phrali)) |
| 118 | | |
| 119 | | ; Run parser once. |
| 120 | | (cog-execute! parser) |
| 121 | | (cog-extract-recursive! phrali) |
| 122 | | ) |
| 123 | | |
| 124 | | ; Example: |
| 125 | | ; (obs-texty "this is a test") |
| 126 | | ; (cog-execute! (ValueOf (ParseNode "ANY") (Predicate "count")) |
| 127 | | |
| 128 | | ; -------------------------------------------------------------- |
| 129 | 37 | ; Demo wrapper: Parse contents of a file. |
| 130 | | (define (obs-file FILE-NAME) |
| 38 | (define (obs-file FILE-NAME PARSER) |
| 131 | 39 | |
| 132 | | ; We don't need to create this over and over; once is enough. |
| 133 | | (define txt-stream |
| 134 | | (ValueOf (Anchor "parse pipe") (Predicate "text src"))) |
| 135 | | (define parser (make-parser txt-stream)) |
| 136 | | |
| 137 | | (define phrali (Open (Type 'TextFileStream) |
| 138 | | (Sensory (string-append "file://" FILE-NAME)))) |
| 40 | (define sensor (Sensory (string-append "file://" FILE-NAME))) |
| 139 | 41 | |
| 140 | 42 | (cog-execute! |
| 141 | | (SetValue (Anchor "parse pipe") (Predicate "text src") phrali)) |
| 43 | (SetValue |
| 44 | (Anchor "parse pipe") (Predicate "text src") |
| 45 | (Open (Type 'TextFileStream) sensor))) |
| 142 | 46 | |
| 143 | 47 | ; Parse only first line of file: |
| 144 | | ; (cog-execute! parser) |
| 48 | ; (cog-execute! PARSER) |
| 49 | |
| 50 | ; Due to a design/implementation failure(??) the above only does |
| 51 | ; one line at a time, although it is sem-intended to stream |
| 52 | ; continuously. But whatever. For now, we loop manually. |
| 145 | 53 | |
| 146 | 54 | ; Loop over all lines. |
| 147 | | (define (looper) (cog-execute! parser) (looper)) |
| 55 | (define (looper) (cog-execute! PARSER) (looper)) |
| 148 | 56 | |
| 149 | 57 | ; At end of file, exception will be thrown. Catch it. Print it. |
| 150 | 58 | (catch #t looper |
| 151 | 59 | (lambda (key . args) (format #t "The end ~A\n" key))) |
| 152 | 60 | |
| 153 | | (cog-extract-recursive! phrali) |
| 61 | ; Cleanup up litter that we don't want in the atomspace. |
| 62 | (cog-extract-recursive! sensor) |
| 154 | 63 | ) |
| 155 | 64 | |
| 156 | 65 | ; -------------------------- |
| 157 | 66 | ; Run the above demo: |
| 158 | | ; (obs-file "/tmp/demo.txt") |
| 159 | 67 | ; |
| 160 | | ; Look at results. Poke around, look at counts. |
| 161 | 68 | #| |
| 69 | (use-modules (opencog persist-rocks)) |
| 70 | (define storage-node (MonoStorageNode "monospace:///tmp/foo.rdb")) |
| 71 | (cog-open storage-node) |
| 72 | (define parser (setup-parser storage-node)) |
| 73 | (obs-file "/tmp/demo.txt" parser) |
| 74 | ; |
| 75 | ; Look at results. Poke around, look at counts. |
| 162 | 76 | (cog-report-counts) |
| 163 | | (cog-execute! (ValueOf (Word "is") (Predicate "count"))) |
| 77 | (cog-execute! (ValueOf (Word "is") (Predicate "*-TruthValueKey-*"))) |
| 78 | (cog-get-atoms 'Edge) |
| 164 | 79 | (cog-execute! (ValueOf |
| 165 | | (Edge (Bond "ANY") (List (Word "is") (Word "a"))) |
| 166 | | (Predicate "count"))) |
| 80 | (car (cog-get-atoms 'Edge)) |
| 81 | (Predicate "*-TruthValueKey-*"))) |
| 167 | 82 | |
| 168 | 83 | ; Erase all words, so we can try again. |
| 169 | 84 | (extract-type 'WordNode) |