1 ; Modified 2 March 1997 by Will Clinger to add graphs-benchmark
2 ; and to expand the four macros below.
3 ; Modified 11 June 1997 by Will Clinger to eliminate assertions
4 ; and to replace a use of "recur" with a named let.
6 ; Performance note: (graphs-benchmark 7) allocates
8 ; 389625 vectors with 2551590 elements
9 ; 56653504 closures (not counting top level and known procedures)
11 (define (graphs-benchmark . rest)
12 (let ((N (if (null? rest) 7 (car rest))))
13 (run-benchmark (string-append "graphs" (number->string N))
24 ; (define-syntax assert
26 ; ((assert test info-rest ...)
31 ; ((deny test info-rest ...)
36 ; ((when test e-first e-rest ...)
41 ; (define-syntax unless
43 ; ((unless test e-first e-rest ...)
55 ; Fold over list elements, associating to the left.
57 (lambda (lst folder state)
60 '(assert (procedure? folder)
70 ; Given the size of a vector and a procedure which
71 ; sends indicies to desired vector elements, create
72 ; and return the vector.
75 '(assert (and (integer? size)
79 '(assert (procedure? f)
83 (let ((x (make-vector size (f 0))))
85 (if (< i size) (begin ; [wdc - was when]
86 (vector-set! x i (f i))
91 (lambda (vec folder state)
92 '(assert (vector? vec)
94 '(assert (procedure? folder)
101 (folder (vector-ref vec i)
108 (proc->vector (vector-length vec)
110 (proc (vector-ref vec i))))))
112 ; Given limit, return the list 0, 1, ..., limit-1.
115 '(assert (and (integer? limit)
129 (cons limit res)))))))
131 ; Fold over the integers [0, limit).
132 (define gnatural-fold
133 (lambda (limit folder state)
134 '(assert (and (integer? limit)
138 '(assert (procedure? folder)
147 ; Iterate over the integers [0, limit).
148 (define gnatural-for-each
149 (lambda (limit proc!)
150 '(assert (and (integer? limit)
154 '(assert (procedure? proc!)
161 (define natural-for-all?
163 '(assert (and (integer? limit)
167 '(assert (procedure? ok?)
175 (define natural-there-exists?
177 '(assert (and (integer? limit)
181 '(assert (procedure? ok?)
185 (and (not (= i limit))
189 (define there-exists?
193 '(assert (procedure? ok?)
197 (and (not (null? lst))
202 ;;; ==== ptfold.ss ====
205 ; Fold over the tree of permutations of a universe.
206 ; Each branch (from the root) is a permutation of universe.
207 ; Each node at depth d corresponds to all permutations which pick the
208 ; elements spelled out on the branch from the root to that node as
209 ; the first d elements.
210 ; Their are two components to the state:
211 ; The b-state is only a function of the branch from the root.
212 ; The t-state is a function of all nodes seen so far.
213 ; At each node, b-folder is called via
214 ; (b-folder elem b-state t-state deeper accross)
215 ; where elem is the next element of the universe picked.
216 ; If b-folder can determine the result of the total tree fold at this stage,
217 ; it should simply return the result.
218 ; If b-folder can determine the result of folding over the sub-tree
219 ; rooted at the resulting node, it should call accross via
220 ; (accross new-t-state)
221 ; where new-t-state is that result.
222 ; Otherwise, b-folder should call deeper via
223 ; (deeper new-b-state new-t-state)
224 ; where new-b-state is the b-state for the new node and new-t-state is
225 ; the new folded t-state.
226 ; At the leaves of the tree, t-folder is called via
227 ; (t-folder b-state t-state accross)
228 ; If t-folder can determine the result of the total tree fold at this stage,
229 ; it should simply return that result.
230 ; If not, it should call accross via
231 ; (accross new-t-state)
232 ; Note, fold-over-perm-tree always calls b-folder in depth-first order.
233 ; I.e., when b-folder is called at depth d, the branch leading to that
234 ; node is the most recent calls to b-folder at all the depths less than d.
235 ; This is a gross efficiency hack so that b-folder can use mutation to
236 ; keep the current branch.
237 (define fold-over-perm-tree
238 (lambda (universe b-folder b-state t-folder t-state)
239 '(assert (list? universe)
241 '(assert (procedure? b-folder)
243 '(assert (procedure? t-folder)
253 (lambda (final-t-state)
256 (t-folder b-state t-state accross)
271 (lambda (new-t-state)
278 (lambda (new-b-state new-t-state)
279 (-*- (fold out cons rest)
286 ;;; ==== minimal.ss ====
289 ; A directed graph is stored as a connection matrix (vector-of-vectors)
290 ; where the first index is the `from' vertex and the second is the `to'
291 ; vertex. Each entry is a bool indicating if the edge exists.
292 ; The diagonal of the matrix is never examined.
293 ; Make-minimal? returns a procedure which tests if a labelling
294 ; of the verticies is such that the matrix is minimal.
295 ; If it is, then the procedure returns the result of folding over
296 ; the elements of the automoriphism group. If not, it returns #F.
297 ; The folding is done by calling folder via
298 ; (folder perm state accross)
299 ; If the folder wants to continue, it should call accross via
300 ; (accross new-state)
301 ; If it just wants the entire minimal? procedure to return something,
302 ; it should return that.
303 ; The ordering used is lexicographic (with #T > #F) and entries
304 ; are examined in the following order:
314 (define make-minimal?
316 '(assert (and (integer? max-size)
321 (proc->vector (+ max-size 1)
324 (make-vector max-size 0)))
325 (lambda (size graph folder state)
326 '(assert (and (integer? size)
328 (<= 0 size max-size))
331 '(assert (vector? graph)
333 '(assert (procedure? folder)
335 (fold-over-perm-tree (vector-ref iotas size)
336 (lambda (perm-x x state deeper accross)
337 (case (cmp-next-vertex graph perm x perm-x)
341 (vector-set! perm x perm-x)
349 (lambda (leaf-depth state accross)
350 '(assert (eqv? leaf-depth size)
353 (folder perm state accross))
356 ; Given a graph, a partial permutation vector, the next input and the next
357 ; output, return 'less, 'equal or 'more depending on the lexicographic
358 ; comparison between the permuted and un-permuted graph.
359 (define cmp-next-vertex
360 (lambda (graph perm x perm-x)
362 (vector-ref graph x))
364 (vector-ref graph perm-x)))
371 (vector-ref from-x y))
373 (vector-ref perm y)))
375 (vector-ref from-perm-x perm-y))
377 (vector-ref (vector-ref graph y)
380 (vector-ref (vector-ref graph perm-y)
396 ; Fold over rooted directed graphs with bounded out-degree.
397 ; Size is the number of verticies (including the root). Max-out is the
398 ; maximum out-degree for any vertex. Folder is called via
399 ; (folder edges state)
400 ; where edges is a list of length size. The ith element of the list is
401 ; a list of the verticies j for which there is an edge from i to j.
402 ; The last vertex is the root.
403 (define fold-over-rdg
404 (lambda (size max-out folder state)
405 '(assert (and (exact? size)
409 '(assert (and (exact? max-out)
413 '(assert (procedure? folder)
420 (make-vector size #F))))
422 (make-vector size '()))
424 (make-vector size 0))
426 (make-minimal? root))
429 (lambda (perm state accross)
430 '(assert (eq? state #T)
440 (lambda (perm state accross)
441 '(assert (eq? state #T)
443 (case (cmp-next-vertex edge? perm root root)
460 (cond ((not (non-root-minimal? vertex))
465 (gnatural-for-each root
467 '(assert (= (vector-ref out-degrees v)
468 (length (vector-ref edges v)))
470 (vector-ref out-degrees v)
471 (vector-ref edges v))))
474 (make-reach? root edges))
476 (vector-ref edge? root)))
488 (cond ((not (or (= v root)
490 (vector-set! from-root v #T)
495 (cons (vector-ref reach? v)
498 (vector-set! from-root v #F)
504 ((and (natural-for-all? root
510 (vector-set! edges root efr)
514 (vector-ref edges i)))
520 (vector-ref edge? vertex)))
530 (vector-set! out-degrees vertex outs)
534 ; no sv->vertex, no vertex->sv
539 (vector-ref edge? sv))
541 (vector-ref out-degrees sv))
543 (if (= sv-out max-out)
549 (vector-ref edges sv)))
550 (vector-set! from-sv vertex #T)
551 (vector-set! out-degrees sv (+ sv-out 1))
553 ; sv->vertex, no vertex->sv
561 (vector-set! from-vertex sv #T)
565 (vector-ref edges vertex)))
567 ; sv->vertex, vertex->sv
573 (cdr (vector-ref edges vertex)))
574 (vector-set! from-vertex sv #F)
576 (vector-set! out-degrees sv sv-out)
577 (vector-set! from-sv vertex #F)
580 (cdr (vector-ref edges sv)))
588 (vector-ref edges vertex)))
589 (vector-set! from-vertex sv #T)
591 ; no sv->vertex, vertex->sv
595 (vector-set! from-vertex sv #F)
598 (cdr (vector-ref edges vertex)))
601 ; Given a vector which maps vertex to out-going-edge list,
602 ; return a vector which gives reachability.
604 (lambda (size vertex->out)
609 (make-vector size #F)))
610 (vector-set! from-v v #T)
613 (vector-set! from-v x #T))
614 (vector-ref vertex->out v))
616 (gnatural-for-each size
620 (gnatural-for-each size
624 (if (vector-ref from-f m); [wdc - was when]
626 (gnatural-for-each size
628 (if (vector-ref from-m t)
629 (begin ; [wdc - was when]
630 (vector-set! from-f t #T)))))))))))))
634 ;;; ==== test input ====
636 ; Produces all directed graphs with N verticies, distinguished root,
637 ; and out-degree bounded by 2, upto isomorphism (there are 44).