;
; edge-pairs.scm
;
; Define an EdgeLink matrix API object.
;
; Copyright (c) 2013, 2014, 2017, 2020 Linas Vepstas
;
; ---------------------------------------------------------------------
; OVERVIEW
; --------
; The object below provides a matrix API to access pairs of Atoms, held
; in an EdgeLink. This API unlocks a suite of tools for computing
; various properties of the matrix, including frequencies, marginal
; probabilities and assorted vector-space properties. By "matrix" it
; is meant a rank-2 sparse matrix, a matrix of (left,right) paris or
; (row, column) pairs. This is exactly the API needed to unlock the
; toolset in the `(use-modules (opencog matrix))` statistical analysis
; subsystem.
;
; To be explicit: a sparse matrix can be encoded in the AtomSpace as
;
; EdgeLink
; PredicateNode "some named relationship"
; ListLink
; SomeAtom "Some thing on the left (row)"
; OtherNode "some other atom on right (column)"
;
; Matrix entries N(x,y) are stored as counts (numbers) on the EdgeLink,
; with the `x` being the left atom, and `y` being the right atom.
;
; Given the generic API, the matrx system will compute marginals N(x,*)
; and N(*,y), a grand total N(*,*) and then probabilities:
; p(x,y) = N(x,y)/N(*,*)
; and from this, various other statistical quantities, such as mutual
; information.
;
; ---------------------------------------------------------------------
(define-public (make-edge-pair-api EDGE-TYPE PRED-NODE
LEFT-TYPE RIGHT-TYPE ANY-LEFT ANY-RIGHT ID NAME)
"
make-edge-pair-api -- Pair access methods for generic pairs.
This implements a matrix object representing atom-pairs, using a
conventional pair encoding. For example, an atom pair can be
represented as:
EdgeLink
PredicateNode \"Some named relation\"
ListLink
SomeAtom \"some thing on left (in a row)\"
OtherNode \"other thing on right (in a column)\"
This Atom (the EdgeLink) can be used to record counts, frequencies,
entropies, etc pertaining to this particular pair, simply by placing
them, as values, on the EdgeLink. The EdgeLink serves as a well-known
location in a sparse rank-2 matrix; arbitrary data can be placed there.
(Historically, the EvaluationLink was used for this; however, the C++
implementation of the EvaluationLink uses 120 Bytes more RAM than
plain Link types. Since \"typical\" Links require 500 Bytes, this is an
additional 120/500 = 25% RAM penalty. The EvaluationLink uses this
memory to store free variables, used during grounded evaluations.)
The EDGE-TYPE should be the Link type encoding the pair; it is
'EdgeLink in the example above.
The PRED-NODE should be the Atom labelling the pair; it is
(PredicateNode \"Some named relation\") in the example above.
The LEFT-TYPE should be the Atom type of the row-atoms.
The RIGHT-TYPE should be the Atom type of the column-atoms.
Atom types are scheme symbols, for example 'ConceptNode.
The left and right types can be Nodes or Links.
The ID should be a short string that serves as a unique id for
this particular matrix. It is used when constructing submatrixes
and filtered variants of this matrix.
The NAME should be a long string describing the contents of this
matrix. It is used as a title when printing summary reports.
The ANY-LEFT and ANY-RIGHT should be atoms where row and column
marginals can be stored. The `AnyNode` is convenient for this
purpose. Thus, for example, marginal probabilities for rows of
genomic data might be encoded as:
EdgeLink
PredicateNode \"Some named relation\"
ListLink
AnyNode \"left-gene\"
GeneNode \"SIRT1\"
The corresponding usage for this object would then be:
(make-edge-pair-api 'EdgeLink (Predicate \"Some named relation\")
'GeneNode 'GeneNode (Any \"left-gene\") (Any \"right-gene\")
\"gene-pairs\" \"Interacting pairs of genes\")
"
(let ((all-pairs '()))
(define (get-left-type) LEFT-TYPE)
(define (get-right-type) RIGHT-TYPE)
(define (get-pair-type) EDGE-TYPE)
; Return the atom holding the count, if it exists, else
; return nil.
(define (get-pair L-ATOM R-ATOM)
(define maybe-list (cog-link 'ListLink L-ATOM R-ATOM))
(if (null? maybe-list) '()
(cog-link EDGE-TYPE PRED-NODE maybe-list)))
; Create an atom to hold the count (if it doesn't exist already).
(define (make-pair L-ATOM R-ATOM)
(cog-new-link EDGE-TYPE PRED-NODE (List L-ATOM R-ATOM)))
; Return the left member of the pair. Given the pair-atom,
; locate the left-side atom.
(define (get-left-element PAIR)
(gadr PAIR))
(define (get-right-element PAIR)
(gddr PAIR))
; Caution: this unconditionally creates the wildcard pair!
(define (get-left-wildcard WORD)
(make-pair ANY-LEFT WORD))
; Caution: this unconditionally creates the wildcard pair!
(define (get-right-wildcard WORD)
(make-pair WORD ANY-RIGHT))
(define (get-wild-wild)
(make-pair ANY-LEFT ANY-RIGHT))
; get-all-pairs - return a list holding all of the observed
; pairs. Caution: this can be obscenely long!
(define (do-get-all-pairs)
; The list of pairs is mostly just the incoming set of the
; predicate node. However, this does include some junk, so
; filter out all rows and columns of the incorrect type.
(filter!
(lambda (pair)
(and
(equal? LEFT-TYPE (cog-type (gadr pair)))
(equal? RIGHT-TYPE (cog-type (gddr pair)))))
(cog-incoming-by-type PRED-NODE EDGE-TYPE)))
(define (get-all-pairs)
(if (null? all-pairs) (set! all-pairs (do-get-all-pairs)))
all-pairs)
; fetch-all-pairs -- fetch all values for atom pairs
; from the currently-open database.
(define (fetch-all-pairs)
(define elapsed-secs (make-elapsed-secs))
(fetch-incoming-by-type PRED-NODE EDGE-TYPE)
(format #t "Elapsed time to load pairs: ~A secs\n"
(elapsed-secs))
)
; Delete the pairs from the atomspace AND the database.
; But only those that are currently in the atomspace are
; deleted; if any are hiding in the database, they will not be
; touched.
(define (delete-all-pairs)
(define elapsed-secs (make-elapsed-secs))
(for-each (lambda (PAIR) (cog-delete-recursive! (gdr PAIR)))
(cog-incoming-set PRED-NODE))
(cog-delete! PRED-NODE)
(cog-delete! ANY-LEFT)
(cog-delete! ANY-RIGHT)
(format #t "Elapsed time to delete pairs: ~A secs\n"
(elapsed-secs))
)
;-------------------------------------------
(define (help)
(format #t
(string-append
"This is the `make-edge-pair-api` object. It provides a matrix API\n"
"for the Atom pairs of the following form:\n"
"\n"
" ~A\n"
" ~A \"~A\"\n"
" ListLink\n"
" ~A ...\n"
" ~A ...\n"
"\n"
"All of the core matrix API methods are provided; these include:\n"
"\n"
" name The string name of the object\n"
" id The string ID of the object\n"
" left-type The type of the row Atoms\n"
" right-type The type of the column Atoms\n"
" pair-type Returns the edge type\n"
" get-pair L R Returns Edge Pred List L R, if it exists, else null\n"
" make-pair L R Unconditionally make Edge Pred List L R\n"
" left-element E Return the row Atom of the Edge E\n"
" right-element E Return the column Atom of the Edge E\n"
" left-wildcard R Return Edge Pred List ANY R\n"
" right-wildcard L Return Edge Pred List L ANY\n"
" wild-wild Return Edge Pred List ANY ANY\n"
" all-pairs Return a list of all non-zero entries in the matrix\n"
" fetch-pairs Fetch all matrix entries from currently-open database\n"
" delete-pairs Delete all pairs for this matrix in the AtomSpace\n"
" help Print this message\n"
" describe Print documentation for make-edge-pair-api\n"
)
EDGE-TYPE (cog-type PRED-NODE) (cog-name PRED-NODE) LEFT-TYPE RIGHT-TYPE)
*unspecified*)
(define (describe)
(display (procedure-property make-edge-pair-api 'documentation)))
;-------------------------------------------
; Methods on the object.
(lambda (message . args)
(apply (case message
((name) (lambda () NAME))
((id) (lambda () ID))
((left-type) get-left-type)
((right-type) get-right-type)
((pair-type) get-pair-type)
((get-pair) get-pair)
((make-pair) make-pair)
((left-element) get-left-element)
((right-element) get-right-element)
((left-wildcard) get-left-wildcard)
((right-wildcard) get-right-wildcard)
((wild-wild) get-wild-wild)
((get-all-elts) get-all-pairs)
((fetch-pairs) fetch-all-pairs)
((delete-pairs) delete-all-pairs)
((provides) (lambda (symb) #f))
((filters?) (lambda () #f))
((help) help)
((describe) describe)
(else (error "Bad method call on make-edge-pair-api:" message)))
args)))
)
; ---------------------------------------------------------------------
(define-public (make-evaluation-pair-api PRED-NODE LEFT-TYPE RIGHT-TYPE
ANY-LEFT ANY-RIGHT ID NAME)
"
make-evaluation-pair-api -- Pair access methods for EvaluationLinks.
See make-edge-pair-api for documentation.
Deprecated; do not use in new code!
"
(make-edge-pair-api 'EvaluationLink
PRED-NODE LEFT-TYPE RIGHT-TYPE ANY-LEFT ANY-RIGHT ID NAME)
)
; ---------------------------------------------------------------------