1 ;; search-expanded.el - network search routines
3 ;; Copyright (C) 2013 Raymond S. Puzio
5 ;; This program is free software: you can redistribute it and/or modify
6 ;; it under the terms of the GNU Affero General Public License as published by
7 ;; the Free Software Foundation, either version 3 of the License, or
8 ;; (at your option) any later version.
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU Affero General Public License for more details.
15 ;; You should have received a copy of the GNU Affero General Public License
16 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
22 ;; We will implement the search as a pipeline which gradually
23 ;; transforms the query into a series of expressions which produce
24 ;; the sought-after result, then evaluate those expressions. To
25 ;; illustrate and explain this process, we will use the following
26 ;; query as an example:
28 ;; This tells you the predicates that apply to the nodes
29 ;; and the network relationships that apply to them. The network relationships
30 ;; function as wildcards.
32 ;; Basic model at the moment triplets that point to other triplets.
41 ;; This asks us to find a funny link from a big blue object to a
42 ;; small green link to the big blue object.
44 ;; The first step of processing is to put the quaerenda in some
45 ;; order so that each item links up with at least one previous item:
57 ;; ((c (green small) ((b snk c) (c src a)))
58 ;; (b (funny) ((b src a)))
61 ;; (Note that the order is reversed due to technicalities of
62 ;; implementing "scheduler" --- that is to say, a is first and does
63 ;; not link to any other variable, b is next and links to only a,
64 ;; whilst c is last and links to both a and b.)
66 ;; At the same time, we have also rearranged things so that the
67 ;; links to previous items to which a given object are listed
68 ;; alongside that object.
70 ;; The next step is to replace the links with the commands which
71 ;; generate a list of such objects:
73 ;; ((c (green small) ((b snk c) (c src a)))
74 ;; (b (funny) ((b src a)))
80 ;; (intersection (list (get-snk b)) (get-forward-links a)))
82 ;; (intersection (get-backward-links a)))
85 ;; This is done using the function tplts2cmd, e.g.
87 ;; (tplts2cmd 'c '((b snk c) (c src a)))
89 ;; => (intersection (list (get-snk b)) (get-forward-links a))
91 ;; Subsequently, we filter over the predicates:
93 ;; ((c (filter '(lambda (c) (and (green c) (small c)))
94 ;; (intersection (list (get-snk b))
95 ;; (get-forward-links))))
96 ;; (b (filter '(lambda (b) (and (funny b)))
97 ;; (intersection (get-backward-links a)))))
99 ;; This is done with the command add-filt:
103 ;; '((b snk c) (c src a)))
105 ;; (c (filter (quote (lambda (c) (and (green c) (small c))))
106 ;; (intersection (list (get-snk b))
107 ;; (get-forward-links a))))
110 ;; This routine calls up the previously described routine tplts2cmd
111 ;; to take care of the third argument.
117 ;; gets processed a little differently because we don't as yet have
118 ;; anything to filter over; instead, we generate the initial list by
119 ;; looping over the current network:
121 ;; (a (let ((ans nil))
123 ;; (when (and (blue (get-content node))
124 ;; (big (get-content node)))
125 ;; (setq ans (cons node ans))))
128 ;; This is done by invoking first2cmd:
130 ;; (first2cmd '(blue big))
135 ;; (donet (quote node)
136 ;; (when (and (blue (get-content node))
137 ;; (big (get-content node)))
138 ;; (setq ans (cons node ans))))
142 ;; '((c (green small) ((b snk c) (c src a)))
143 ;; (b (funny) ((b src a)))
148 ;; ((c (filter (quote (lambda (c) (and (green c) (small c))))
149 ;; (intersection (list (get-snk b))
150 ;; (get-forward-links a))))
151 ;; (b (filter (quote (lambda (b) (and (funny b))))
152 ;; (intersection (get-forward-links a))))
153 ;; (a (let ((ans nil))
154 ;; (donet (quote node)
155 ;; (when (and (blue (get-content node))
156 ;; (big (get-content node)))
157 ;; (setq ans (cons node ans))))
160 ;; To carry out these instructions in the correct order and generate
161 ;; a set of variable assignments, we employ the matcher function.
162 ;; Combining this last layer, we have the complete pipeline:
172 ;; '((a blue big)))))
174 ;; This combination of operations is combined into the search
185 ;; Having described what the functions are supposed to do and how
186 ;; they work together, we now proceed to implement them.
188 ;; The scheduler function takes a list search query and rearranges it
189 ;; into an order suitable for computing the answer to that query.
190 ;; Specifically, a search query is a pair of lists --- the first list
191 ;; consists of lists whose heads are names of variables and whose
192 ;; tails are predicates which the values of the variables should
193 ;; satisfy and the second list consists of triples indicating the
194 ;; relations between the values of the variables.
196 ;; new-nodes is a list of items of the form (node &rest property)
197 ;; links is a list of triplets
198 ;; sched is a list whose items consist of triplets of the
199 ;; form (node (&rest property) (&rest link))
203 ;; We start with some preliminary definitions and generalities.
207 ;; Returns the subset of stuff which satisfies the predicate pred.
209 (defun filter (pred stuff
)
211 (dolist (item stuff
(reverse ans
))
212 (if (funcall pred item
)
213 (setq ans
(cons item ans
))
216 ;; Set-theoretic intersection operation.
217 ;; More general than the version coming from the `cl' package
218 (defun intersection (&rest arg
)
219 (cond ((null arg
) nil
)
220 ((null (cdr arg
)) (car arg
))
222 (dolist (elmt (car arg
) ans
)
223 (let ((remainder (cdr arg
)))
224 (while (and remainder
225 (member elmt
(car remainder
)))
226 (setq remainder
(cdr remainder
))
227 (when (null remainder
)
228 (setq ans
(cons elmt ans
))))))))))
231 (defun scheduler (new-nodes links sched
)
232 ;; If done, return answer.
236 ;; New nodes yet to be examined.
237 (remaining-nodes new-nodes
)
238 ;; Element of remaining-nodes currently
239 ;; under consideration.
241 ;; List of links between candidate and old-nodes.
243 ;; List of nodes already scheduled.
244 (old-nodes (mapcar 'car sched
)))
245 ;; Loop through new nodes until find one linked
248 ;; Look at the next possible node.
249 (setq candidate
(car remaining-nodes
))
250 (setq remaining-nodes
(cdr remaining-nodes
))
251 ;; Find the old nodes linking to the candidate
252 ;; node and record the answer in "ties".
254 ;; Pick out the triplets
257 ;; whose first element is the node
258 ;; under consideration and whose third
259 ;; element is already on the list
260 (and (eq (first x
) (car candidate
))
261 (member (third x
) old-nodes
))
263 (and (member (first x
) old-nodes
)
264 (eq (third x
) (car candidate
)))))
266 ;; Recursively add the rest of the nodes.
267 (scheduler (remove candidate new-nodes
)
269 (cons (list (car candidate
)
274 (defun tplts2cmd (var tplts
)
278 (cond ((and (eq (third tplt
) var
)
279 (eq (second tplt
) 'src
))
280 `(get-flk ,(first tplt
)))
281 ((and (eq (third tplt
) var
)
282 (eq (second tplt
) 'snk
))
283 `(get-blk ,(first tplt
)))
284 ((and (eq (first tplt
) var
)
285 (eq (second tplt
) 'src
))
286 `(list (get-src ,(third tplt
))))
287 ((and (eq (first tplt
) var
)
288 (eq (second tplt
) 'snk
))
289 `(list (get-snk ,(third tplt
))))
293 (defun add-filt (var preds tplts
)
301 (list 'get-txt var
)))
303 ,(tplts2cmd var tplts
))))
305 (defun first2cmd (preds)
307 (dolist (node (get-ids) ans
)
312 (cons pred
'((get-txt node
))))
314 (setq ans
(cons node ans
))))))
316 (defun query2cmd (query)
317 (let ((backwards (reverse query
)))
320 (list (caar backwards
)
321 (first2cmd (cdar backwards
)))
324 (add-filt (first x
) (second x
) (third x
)))
327 (defun matcher (assgmt reqmts
)
328 (if (null reqmts
) (list assgmt
)
331 (matcher (cons (list (caar reqmts
) x
) assgmt
)
336 (cdar reqmts
)))))))))
338 (defun search (query)
345 (list (caar query
)))))))
347 ;; Here are some examples unrelated to what comes up in serching
348 ;; triplets which illustrate how matcher works:
352 ;; (z (list (+ x y) (- y x)))))
354 ;; (((z 2) (y 1) (x 1))
355 ;; ((z 0) (y 1) (x 1))
356 ;; ((z 4) (y 3) (x 1))
357 ;; ((z 2) (y 3) (x 1)))
363 ;; (z (list (+ x y) (- y x)))))
367 ;; (((z 2) (y 1) (x 1))
368 ;; ((z 0) (y 1) (x 1))
369 ;; ((z 4) (y 3) (x 1))
370 ;; ((z 2) (y 3) (x 1)))