2 ;;; GRAPHS - graph theory package for Maxima
4 ;;; Copyright (C) 2007 Andrej Vodopivec <andrej.vodopivec@gmail.com>
6 ;;; This program is free software; you can redistribute it and/or modify
7 ;;; it under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 2 of the License, or
9 ;;; (at your option) any later version.
11 ;;; This program is distributed in the hope that it will be useful,
12 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with this program; if not, write to the Free Software
18 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25 ;;; Demoucron planarity test algorithm (a simple quadratic-time
26 ;;; planarity test algorithm)
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30 ;;;;;;;;;;;;;;;;;;;;;;;;;
35 (defvar $demoucron_debug nil
)
37 ;;;;;;;;;;;;;;;;;;;;;;;;;
39 ;; finds a cycle in g - g should be connected
44 (defvar *dfsnum-curr
*)
47 (defun find-cycle (gr)
48 (let ((*back-edges
* ())
49 (*dfsnum
* (make-hash-table))
50 (*dfs-prev
* (make-hash-table))
52 (v (first (vertices gr
)))
54 (setf (gethash v
*dfs-prev
*) v
)
56 (if (null *back-edges
*)
59 (let ((u (caar *back-edges
*))
60 (v (cadar *back-edges
*)))
61 (loop while
(not (= u v
)) do
63 (setq u
(gethash u
*dfs-prev
*)))
67 (defun dfs-find-cycle (v gr
)
68 (setf (gethash v
*dfsnum
*) *dfsnum-curr
*)
70 (loop for u in
(neighbors v gr
) do
71 (if (null (gethash u
*dfsnum
*))
73 (setf (gethash u
*dfs-prev
*) v
)
74 (dfs-find-cycle u gr
))
75 (when (not (= u
(gethash v
*dfs-prev
*)))
76 (push (list u v
) *back-edges
*)))))
78 (defmfun $find_cycle
(gr)
79 (require-graph 'find_cycle
1 gr
)
80 (let ((cycle (find-cycle gr
)))
81 `((mlist simp
) ,@cycle
)))
83 ;;;;;;;;;;;;;;;;;;;;;;;;;
85 ;; demoucron algorithm for a biconnected graph gr
95 ;; finds facial walks of h (running time o(E(h)))
98 (defvar *facial-walks
*)
100 (defun find-facial-walks (edges)
101 (let ((visited-edges (make-hash-table :test
#'equal
)))
102 (loop for e in edges do
103 (when (null (gethash e visited-edges
))
107 (z (gethash (list v u
) *embedding
*)))
108 (setf (gethash (list u v
) visited-edges
) t
)
111 (while (not (and (= u
(first e
))
113 (setf (gethash (list u v
) visited-edges
) t
)
115 (setq z
(gethash (list v u
) *embedding
*))
118 (push walk
*facial-walks
*)))) ))
122 ;; finds the bridges in g-h
123 ;; - a bridge is a list (vertices-of-the-bridge attachements-of-the-bridge)
127 (defvar *current-bridge
*)
128 (defvar *current-attachements
*)
129 (defvar *visited-vertices
*)
131 (defun find-bridges (gr)
133 (setq *visited-vertices
* (make-hash-table))
134 (loop for v in
*g-vertices
* do
135 (unless (gethash v
*visited-vertices
*)
136 (setq *current-bridge
* (list v
))
137 (setq *current-attachements
* ())
138 (dfs-find-bridge v gr
)
139 (dolist (v *current-attachements
*)
140 (remhash v
*visited-vertices
*))
141 (push (list *current-bridge
* *current-attachements
*) *bridges
*)))
142 (loop for e in
(edges gr
) do
143 (if (and (subsetp e
*h-vertices
*)
144 (null (gethash e
*h-edges
*)))
145 (push (list e e
) *bridges
*))))
147 (defun dfs-find-bridge (v gr
)
148 (setf (gethash v
*visited-vertices
*) t
)
149 (loop for u in
(neighbors v gr
) do
150 (when (null (gethash u
*visited-vertices
*))
151 (push u
*current-bridge
*)
152 (if (member u
*h-vertices
*)
154 (setf (gethash u
*visited-vertices
*) t
)
155 (push u
*current-attachements
*))
156 (dfs-find-bridge u gr
)))))
160 ;; for each bridge find facial walks in which the bridge can be embedded
161 ;; - a bridge is a list (vertices-of-the-bridge attachements-of-the-bridge)
164 (defvar *available-faces
*)
166 (defun match-bridges-to-walks ()
167 (setq *available-faces
* (make-hash-table :test
#'equal
))
168 (loop for b in
*bridges
* do
170 (loop for w in
*facial-walks
* do
171 (if (subsetp (second b
) w
)
173 (setf (gethash b
*available-faces
*) walks
))))
177 ;; finds a path between attachment vertices in a bridge
180 (defvar *dfs-visited
*)
182 (defun find-path (bridge gr
)
183 (when (= (length (first bridge
)) 2)
184 (return-from find-path
(first bridge
)))
185 (let ((*dfs-prev
* (make-hash-table))
186 (*dfs-visited
* (make-hash-table))
187 (attachements (second bridge
)))
188 (dfs-find-path (first attachements
) (car bridge
) gr
)
189 ;; should not happen since graphs are biconnected!
190 (if (< (length attachements
) 2)
191 (merror "Too few attachements"))
192 (let ((u (second attachements
))
194 (loop while
(not (null (gethash u
*dfs-prev
*))) do
196 (setq u
(gethash u
*dfs-prev
*)))
200 (defun dfs-find-path (v bridge gr
)
201 (setf (gethash v
*dfs-visited
*) t
)
202 (loop for u in
(neighbors v gr
) do
203 (unless (gethash (list v u
) *h-edges
*)
204 (when (and (null (gethash u
*dfs-visited
*))
206 (setf (gethash u
*dfs-prev
*) v
)
207 (unless (member u
*h-vertices
*)
208 (dfs-find-path u bridge gr
))))))
212 ;; ads the path to h and updates the embedding of h
215 (defun embedd-path (path face
)
218 (let ((inner (cdr path
)))
219 (loop while
(not (null (cdr inner
))) do
220 (push (car inner
) *h-vertices
*)
221 (setq *g-vertices
* (remove (car inner
) *g-vertices
*))
222 (setq inner
(cdr inner
))))
224 (loop while
(not (null (cdr inner
))) do
225 (setf (gethash (list (first inner
) (second inner
)) *h-edges
*) t
)
226 (setf (gethash (list (second inner
) (first inner
)) *h-edges
*) t
)
227 (setq inner
(cdr inner
))))
229 ;; add vertices in the inside of the path into the embedding
230 (let ((path-embedd path
))
231 (loop while
(not (null (cddr path-embedd
))) do
232 (let ((u (car path-embedd
))
233 (v (cadr path-embedd
))
234 (z (caddr path-embedd
)))
235 (setf (gethash (list v z
) *embedding
*) u
)
236 (setf (gethash (list v u
) *embedding
*) z
))
237 (setq path-embedd
(cdr path-embedd
))))
239 ;; add the first vertex into the embedding
240 (let ((a (first path
))
242 (rface (reverse face
)))
245 (let ((c (second face
)))
246 (setf (gethash (list a c
) *embedding
*) b
)
247 (setf (gethash (list a b
) *embedding
*) (first rface
))))
249 (let ((c (second rface
)))
250 (setf (gethash (list a b
) *embedding
*) c
)
251 (setf (gethash (list a
(first face
)) *embedding
*) b
)))
254 (loop while
(not (= a
(second face
))) do
255 (setq face
(cdr face
)))
256 (setf (gethash (list a
(third face
)) *embedding
*) b
)
257 (setf (gethash (list a b
) *embedding
*) (first face
))))))
259 ;; add the second vertex into the embedding
260 (setq path
(reverse path
))
261 (let ((a (first path
))
263 (rface (reverse face
)))
266 (let ((c (second face
)))
267 (setf (gethash (list a c
) *embedding
*) b
)
268 (setf (gethash (list a b
) *embedding
*) (first rface
))))
270 (let ((c (second rface
)))
271 (setf (gethash (list a b
) *embedding
*) c
)
272 (setf (gethash (list a
(first face
)) *embedding
*) b
)))
275 (loop while
(not (= a
(second face
))) do
276 (setq face
(cdr face
)))
277 (setf (gethash (list a
(third face
)) *embedding
*) b
)
278 (setf (gethash (list a b
) *embedding
*) (first face
)))))) )
281 ;;;;;;;;;;;;;;;;;;;;;;
283 ;; this is the demoucron planarity test algorithm
284 ;; - g should be 2-connected
287 (defun demoucron (g return-walks
)
289 (when (> ($graph_size g
) (- (* 3 ($graph_order g
)) 6))
290 (return-from demoucron nil
))
294 (*h-edges
* (make-hash-table :test
#'equal
))
295 (*g-vertices
* (cdr ($vertices g
)))
296 (*embedding
* (make-hash-table :test
#'equal
)))
298 ;; find a cycle - assumes there are no degree one vertices!
299 (setq *h-vertices
* (find-cycle g
))
300 (let ((vrt *h-vertices
*))
301 (loop while
(not (null vrt
)) do
302 (setf (gethash (list (first vrt
) (second vrt
)) *h-edges
*) t
)
303 (setf (gethash (list (second vrt
) (first vrt
)) *h-edges
*) t
)
304 (setq vrt
(cdr vrt
))))
305 (let ((v (first *h-vertices
*))
306 (u (first (reverse *h-vertices
*))))
307 (setf (gethash (list u v
) *h-edges
*) t
)
308 (setf (gethash (list v u
) *h-edges
*) t
))
309 (dolist (v *h-vertices
*)
310 (setq *g-vertices
* (remove v
*g-vertices
*)))
313 (let ((cycle *h-vertices
*))
314 (loop while
(not (null (cdr cycle
))) do
315 (let ((u (car cycle
))
317 (z (if (null (caddr cycle
)) (car *h-vertices
*) (caddr cycle
))))
318 (setf (gethash (list v z
) *embedding
*) u
)
319 (setf (gethash (list v u
) *embedding
*) z
))
320 (setq cycle
(cdr cycle
))))
321 (let ((u (car (reverse *h-vertices
*)))
322 (v (car *h-vertices
*))
323 (z (cadr *h-vertices
*)))
324 (setf (gethash (list v z
) *embedding
*) u
)
325 (setf (gethash (list v u
) *embedding
*) z
))
327 ;; find the bridges in g-h
331 (setq *facial-walks
* ())
332 (find-facial-walks (list (list (first *h-vertices
*) (second *h-vertices
*))
333 (list (second *h-vertices
*) (first *h-vertices
*))))
335 (while (not (null *bridges
*))
337 (when $demoucron_debug
338 (print "++++++++++++++++++")
339 (print "--- facial walks:")
340 (print *facial-walks
*)
341 (print "--- bridges:")
342 (mapcar #'print
*bridges
*))
343 ;; for each bridge find facial walks in which it can be embedded
344 (match-bridges-to-walks)
345 ;; select the bridge with the smallest number of available walks
346 (let ((bridge (first *bridges
*))
348 ;; find the bridge with the smallest number of possible facial walks
349 (loop for b in
*bridges
* do
350 (when (< (length (gethash b
*available-faces
*))
351 (length (gethash bridge
*available-faces
*)))
353 (when $demoucron_debug
354 (print "--- embedding:")
356 ;; if the bridge can't be embedded, the graph is not planar
357 (if (= 0 (length (gethash bridge
*available-faces
* bridge
)))
358 (return-from demoucron nil
))
359 ;; find a path in the bridge
360 (setq path
(find-path bridge g
))
362 (embedd-path path
(first (gethash bridge
*available-faces
*)))
363 (when $demoucron_debug
366 (setq *facial-walks
* (remove (first (gethash bridge
*available-faces
*)) *facial-walks
*))
367 ;; find the facial walks of the embedding of h
368 (find-facial-walks (list (list (first path
) (second path
))
369 (list (second path
) (first path
))))
373 ;; if we come here we have embedded the graph into the plane
375 (cons '(mlist simp
) (mapcar #'(lambda (u) (cons '(mlist simp
) u
)) *facial-walks
*))
378 (defmfun $planar_embedding
(gr)
379 (require-graph 'planar_embedding
1 gr
)
380 (unless ($is_biconnected gr
)
381 ($error
"planar_embedding: the graph is not biconnected."))
384 (defmfun $is_planar
(gr)
385 (require-graph 'is_planar
1 gr
)
386 (when (< ($graph_order gr
) 5)
387 (return-from $is_planar t
))
388 (when (> ($graph_size gr
) (- (* 3 ($graph_order gr
)) 6))
389 (return-from $is_planar nil
))
390 (unless ($is_connected gr
)
391 (return-from $is_planar
(is-planar-unconnected gr
)))
392 (when (< ($graph_size gr
) ($graph_order gr
)) ;; gr is a tree
393 (return-from $is_planar t
))
394 (let ((bicomponents ($biconnected_components gr
)))
395 (loop for c in
(cdr bicomponents
) do
397 (unless (demoucron ($induced_subgraph c gr
) nil
)
398 (return-from $is_planar nil
))))
401 (defun is-planar-unconnected (g)
402 (loop for c in
(cdr ($connected_components g
)) do
403 (unless ($is_planar
($induced_subgraph c g
))
404 (return-from is-planar-unconnected nil
)))