;
; full-pipeline.scm
;
; Complete disambiguation pipeline:
; 1. Parse sentence
; 2. Identify ambiguous words
; 3. Compute vagueness
; 4. Generate discriminating queries
; 5. Execute queries against context
; 6. Aggregate evidence
; 7. Return result OR clarifying question
;
(use-modules (srfi srfi-1))
(use-modules (ice-9 format))
(use-modules (ice-9 regex))
(display "═══════════════════════════════════════════════════════════════════\n")
(display " FULL DISAMBIGUATION PIPELINE \n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
;; ═══════════════════════════════════════════════════════════════════
;; KNOWLEDGE BASE (simulating AtomSpace storage)
;; ═══════════════════════════════════════════════════════════════════
;; Word senses with priors
(define word-senses (make-hash-table))
(hash-set! word-senses "bank"
'((FINANCIAL . 0.75) (GEOGRAPHIC . 0.20)))
(hash-set! word-senses "park"
'((RECREATIONAL . 0.50) (PLACE-NAME . 0.40) (VERB . 0.10)))
(hash-set! word-senses "run"
'((MOVE-FAST . 0.50) (OPERATE . 0.35) (MANAGE . 0.15)))
(hash-set! word-senses "plant"
'((VEGETATION . 0.55) (FACTORY . 0.35) (VERB-PLACE . 0.10)))
;; PMI values: (word sense) → pmi-value
(define pmi-table (make-hash-table))
;; BANK discriminators
(hash-set! pmi-table '("money" FINANCIAL) 5.2)
(hash-set! pmi-table '("money" GEOGRAPHIC) -2.0)
(hash-set! pmi-table '("deposit" FINANCIAL) 4.8)
(hash-set! pmi-table '("deposit" GEOGRAPHIC) -1.5)
(hash-set! pmi-table '("withdraw" FINANCIAL) 4.5)
(hash-set! pmi-table '("withdraw" GEOGRAPHIC) -2.0)
(hash-set! pmi-table '("ATM" FINANCIAL) 5.5)
(hash-set! pmi-table '("ATM" GEOGRAPHIC) -3.0)
(hash-set! pmi-table '("account" FINANCIAL) 4.2)
(hash-set! pmi-table '("account" GEOGRAPHIC) -1.0)
(hash-set! pmi-table '("river" FINANCIAL) -1.5)
(hash-set! pmi-table '("river" GEOGRAPHIC) 4.5)
(hash-set! pmi-table '("water" FINANCIAL) -0.5)
(hash-set! pmi-table '("water" GEOGRAPHIC) 4.2)
(hash-set! pmi-table '("swim" FINANCIAL) -2.5)
(hash-set! pmi-table '("swim" GEOGRAPHIC) 3.9)
(hash-set! pmi-table '("shore" FINANCIAL) -2.0)
(hash-set! pmi-table '("shore" GEOGRAPHIC) 4.0)
(hash-set! pmi-table '("fish" FINANCIAL) -1.5)
(hash-set! pmi-table '("fish" GEOGRAPHIC) 3.5)
(hash-set! pmi-table '("mud" FINANCIAL) -2.0)
(hash-set! pmi-table '("mud" GEOGRAPHIC) 3.0)
;; Neutral words
(hash-set! pmi-table '("went" FINANCIAL) 0.2)
(hash-set! pmi-table '("went" GEOGRAPHIC) 0.1)
(hash-set! pmi-table '("walked" FINANCIAL) 0.3)
(hash-set! pmi-table '("walked" GEOGRAPHIC) 0.5)
;; PLANT discriminators
(hash-set! pmi-table '("tree" VEGETATION) 4.0)
(hash-set! pmi-table '("tree" FACTORY) -1.5)
(hash-set! pmi-table '("flower" VEGETATION) 4.2)
(hash-set! pmi-table '("flower" FACTORY) -2.0)
(hash-set! pmi-table '("garden" VEGETATION) 3.8)
(hash-set! pmi-table '("garden" FACTORY) -1.0)
(hash-set! pmi-table '("factory" VEGETATION) -2.0)
(hash-set! pmi-table '("factory" FACTORY) 4.5)
(hash-set! pmi-table '("manufacturing" VEGETATION) -2.5)
(hash-set! pmi-table '("manufacturing" FACTORY) 4.8)
(hash-set! pmi-table '("workers" VEGETATION) -1.0)
(hash-set! pmi-table '("workers" FACTORY) 3.5)
;; Agent type priors
(define agent-priors (make-hash-table))
(hash-set! agent-priors '(penguin swim) 0.85)
(hash-set! agent-priors '(penguin GEOGRAPHIC) 0.90)
(hash-set! agent-priors '(penguin FINANCIAL) 0.001)
(hash-set! agent-priors '(human walk) 0.95)
(hash-set! agent-priors '(human GEOGRAPHIC) 0.30)
(hash-set! agent-priors '(human FINANCIAL) 0.70)
(hash-set! agent-priors '(fish swim) 0.99)
(hash-set! agent-priors '(fish GEOGRAPHIC) 0.999)
(hash-set! agent-priors '(fish FINANCIAL) 0.0001)
;; ═══════════════════════════════════════════════════════════════════
;; UTILITY FUNCTIONS
;; ═══════════════════════════════════════════════════════════════════
(define (get-pmi word sense)
(hash-ref pmi-table (list word sense) 0.0))
(define (get-senses word)
(hash-ref word-senses word #f))
(define (get-agent-prior agent property)
(hash-ref agent-priors (list agent property) 0.5))
(define (tokenize text)
(let* ((lower (string-downcase text))
(clean (regexp-substitute/global #f "[.,!?'\"]" lower 'pre "" 'post)))
(filter (lambda (s) (> (string-length s) 0))
(string-split clean #\space))))
(define (word-in-context? word context-words)
(member word context-words))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 1: PARSE AND IDENTIFY AMBIGUOUS WORDS
;; ═══════════════════════════════════════════════════════════════════
(define (find-ambiguous-words sentence)
(let* ((words (tokenize sentence))
(ambiguous '()))
(for-each
(lambda (word)
(let ((senses (get-senses word)))
(when senses
(set! ambiguous (cons (cons word senses) ambiguous)))))
words)
(reverse ambiguous)))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 2: COMPUTE EVIDENCE FROM SENTENCE
;; ═══════════════════════════════════════════════════════════════════
(define (compute-sentence-evidence ambig-word sentence-words)
(let* ((word (car ambig-word))
(senses (cdr ambig-word))
(evidence (make-hash-table)))
;; Initialize evidence for each sense
(for-each
(lambda (sense-pair)
(hash-set! evidence (car sense-pair) 0.0))
senses)
;; Add PMI from each word in sentence
(for-each
(lambda (w)
(when (not (equal? w word))
(for-each
(lambda (sense-pair)
(let* ((sense (car sense-pair))
(pmi (get-pmi w sense))
(current (hash-ref evidence sense 0.0)))
(when (not (= pmi 0.0))
(hash-set! evidence sense (+ current pmi)))))
senses)))
sentence-words)
evidence))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 3: COMPUTE VAGUENESS
;; ═══════════════════════════════════════════════════════════════════
(define (compute-vagueness evidence)
(let* ((values (hash-map->list (lambda (k v) (abs v)) evidence))
(total (apply + values)))
(/ 1.0 (+ 1.0 total))))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 4: GENERATE DISCRIMINATING QUERIES
;; ═══════════════════════════════════════════════════════════════════
(define (get-discriminators word senses)
"Find words that discriminate between senses"
(let ((all-probe-words '("money" "deposit" "withdraw" "ATM" "account"
"river" "water" "swim" "shore" "fish" "mud"
"tree" "flower" "garden" "factory" "manufacturing" "workers"))
(discriminators '()))
(for-each
(lambda (probe)
(let* ((pmis (map (lambda (s) (cons (car s) (get-pmi probe (car s)))) senses))
(max-pmi (apply max (map cdr pmis)))
(min-pmi (apply min (map cdr pmis)))
(d-power (- max-pmi min-pmi)))
(when (> d-power 3.0) ;; Threshold for useful discriminator
(let ((best-sense (car (find (lambda (p) (= (cdr p) max-pmi)) pmis))))
(set! discriminators
(cons (list probe d-power best-sense) discriminators))))))
all-probe-words)
(sort discriminators (lambda (a b) (> (cadr a) (cadr b))))))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 5: EXECUTE QUERIES AGAINST CONTEXT
;; ═══════════════════════════════════════════════════════════════════
(define (search-context discriminators context-words)
"Search context for discriminating words, return evidence"
(let ((found '()))
(for-each
(lambda (disc)
(let ((probe-word (car disc))
(d-power (cadr disc))
(points-to (caddr disc)))
(when (word-in-context? probe-word context-words)
(set! found (cons disc found)))))
discriminators)
found))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 6: AGGREGATE EVIDENCE
;; ═══════════════════════════════════════════════════════════════════
(define (aggregate-evidence sentence-evidence context-evidence senses priors)
"Combine all evidence sources"
(let ((final-evidence (make-hash-table)))
;; Start with priors
(for-each
(lambda (sense-pair)
(let ((sense (car sense-pair))
(prior (cdr sense-pair)))
(hash-set! final-evidence sense (* (log (+ prior 0.001)) 1.0))))
senses)
;; Add sentence evidence
(hash-for-each
(lambda (sense value)
(hash-set! final-evidence sense
(+ (hash-ref final-evidence sense 0) value)))
sentence-evidence)
;; Add context evidence
(for-each
(lambda (found-disc)
(let* ((probe-word (car found-disc))
(points-to (caddr found-disc)))
(for-each
(lambda (sense-pair)
(let* ((sense (car sense-pair))
(pmi (get-pmi probe-word sense))
(current (hash-ref final-evidence sense 0)))
(hash-set! final-evidence sense (+ current pmi))))
senses)))
context-evidence)
final-evidence))
;; ═══════════════════════════════════════════════════════════════════
;; STEP 7: MAKE DECISION
;; ═══════════════════════════════════════════════════════════════════
(define (decide final-evidence vagueness senses ambig-word)
"Decide: return sense, or generate clarifying question"
(let* ((sorted (sort (hash-map->list cons final-evidence)
(lambda (a b) (> (cdr a) (cdr b)))))
(best (car sorted))
(second (if (> (length sorted) 1) (cadr sorted) (cons 'NONE -999)))
(margin (- (cdr best) (cdr second)))
(word (car ambig-word)))
(cond
;; High confidence: clear winner
((and (< vagueness 0.3) (> margin 3.0))
(list 'RESOLVED (car best) (cdr best)
(format #f "~a = ~a (confidence: HIGH)" word (car best))))
;; Medium confidence: weak preference
((and (< vagueness 0.5) (> margin 1.0))
(list 'RESOLVED-WEAK (car best) (cdr best)
(format #f "~a = ~a (confidence: MEDIUM, might need verification)"
word (car best))))
;; Low confidence: generate question
(else
(let* ((sense-names (map car senses))
(question (format #f "When you mentioned '~a', did you mean:\n" word))
(options (map (lambda (s i)
(format #f " (~a) ~a\n" (+ i 1) s))
sense-names (iota (length sense-names)))))
(list 'NEED-CLARIFICATION #f 0
(string-append question (apply string-append options))))))))
;; ═══════════════════════════════════════════════════════════════════
;; MAIN PIPELINE
;; ═══════════════════════════════════════════════════════════════════
(define (disambiguate sentence context)
"Full disambiguation pipeline"
(display "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
(format #t "INPUT SENTENCE: ~a\n" sentence)
(format #t "CONTEXT: ~a\n\n" (if (null? context) "(none)"
(string-join context " | ")))
(let* ((sentence-words (tokenize sentence))
(context-words (apply append (map tokenize context)))
(all-context (append sentence-words context-words))
(ambiguous (find-ambiguous-words sentence)))
(if (null? ambiguous)
(begin
(display "No ambiguous words found.\n")
(display "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"))
(for-each
(lambda (ambig-word)
(let* ((word (car ambig-word))
(senses (cdr ambig-word)))
(format #t "AMBIGUOUS WORD: '~a'\n" word)
(format #t "POSSIBLE SENSES: ~a\n\n" (map car senses))
;; Step 2: Sentence evidence
(display "STEP 1: Computing evidence from sentence...\n")
(let ((sent-evidence (compute-sentence-evidence ambig-word sentence-words)))
(hash-for-each
(lambda (sense value)
(format #t " ~a: ~,2f\n" sense value))
sent-evidence)
;; Step 3: Vagueness
(let ((vagueness (compute-vagueness sent-evidence)))
(format #t "\nVAGUENESS: ~,2f " vagueness)
(cond
((< vagueness 0.2) (display "(LOW - can resolve)\n"))
((< vagueness 0.4) (display "(MEDIUM - might need context)\n"))
(else (display "(HIGH - need more information)\n")))
;; Step 4: Get discriminators
(display "\nSTEP 2: Finding discriminating words...\n")
(let ((discriminators (get-discriminators word senses)))
(for-each
(lambda (d)
(format #t " '~a' (D=~,1f) → ~a\n" (car d) (cadr d) (caddr d)))
(take discriminators (min 6 (length discriminators))))
;; Step 5: Search context
(display "\nSTEP 3: Searching context for discriminators...\n")
(let ((found (search-context discriminators all-context)))
(if (null? found)
(display " No discriminating words found in context.\n")
(for-each
(lambda (f)
(format #t " FOUND: '~a' → ~a\n" (car f) (caddr f)))
found))
;; Step 6: Aggregate
(display "\nSTEP 4: Aggregating all evidence...\n")
(let* ((final-evidence (aggregate-evidence
sent-evidence found senses senses))
(final-vagueness (compute-vagueness final-evidence)))
(hash-for-each
(lambda (sense value)
(format #t " ~a: ~,2f\n" sense value))
final-evidence)
(format #t " Final vagueness: ~,2f\n" final-vagueness)
;; Step 7: Decide
(display "\nSTEP 5: Making decision...\n")
(let ((decision (decide final-evidence final-vagueness senses ambig-word)))
(display "\n")
(case (car decision)
((RESOLVED)
(display "┌─────────────────────────────────────────────────────────────┐\n")
(format #t "│ ✓ RESOLVED: ~a\n" (cadddr decision))
(display "└─────────────────────────────────────────────────────────────┘\n"))
((RESOLVED-WEAK)
(display "┌─────────────────────────────────────────────────────────────┐\n")
(display "│ ~ RESOLVED (weak): ")
(display (cadddr decision))
(newline)
(display "└─────────────────────────────────────────────────────────────┘\n"))
((NEED-CLARIFICATION)
(display "┌─────────────────────────────────────────────────────────────┐\n")
(display "│ ? CLARIFICATION NEEDED: │\n")
(display "│ │\n")
(for-each
(lambda (line)
(format #t "│ ~a" line))
(string-split (cadddr decision) #\newline))
(display "│ │\n")
(display "└─────────────────────────────────────────────────────────────┘\n")))))))))))
ambiguous))
(display "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n")))
;; ═══════════════════════════════════════════════════════════════════
;; TEST CASES
;; ═══════════════════════════════════════════════════════════════════
(display "═══════════════════════════════════════════════════════════════════\n")
(display " TEST CASES \n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
;; Test 1: Clear disambiguation from sentence alone
(display "TEST 1: Sentence with strong internal evidence\n")
(disambiguate
"I deposited money at the bank."
'())
;; Test 2: Needs context to disambiguate
(display "TEST 2: Ambiguous sentence, context helps\n")
(disambiguate
"I went to the bank."
'("We had been hiking along the river all day."
"The water looked so refreshing."))
;; Test 3: Ambiguous sentence, no context
(display "TEST 3: Ambiguous sentence, no context (should ask question)\n")
(disambiguate
"I went to the bank."
'())
;; Test 4: Different ambiguous word
(display "TEST 4: Different ambiguous word (plant)\n")
(disambiguate
"The plant was huge."
'("The factory had been operating for decades."
"Hundreds of workers arrived each morning."))
;; Test 5: Context doesn't help
(display "TEST 5: Context present but doesn't disambiguate\n")
(disambiguate
"I went to the bank."
'("It was Tuesday."
"Later I went home."))
;; Test 6: Strong context opposite to priors
(display "TEST 6: Context overrides priors\n")
(disambiguate
"I walked to the bank."
'("The river was beautiful in the morning light."
"I wanted to dip my feet in the cool water."
"Some fish were swimming near the shore."))