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 ;;; matching algorithms
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30 (defmfun $max_matching
(gr)
31 (require-graph 'maximum_matching
1 gr
)
32 (let ((partition (cdr ($bipartition gr
))))
34 (maximum-matching-general gr
)
35 (maximum-matching-bipartite gr
(cdr (car partition
)) (cdr (cadr partition
))))))
42 (defun maximum-matching-bipartite (gr a b
&optional cover
)
43 (let ((matching (make-hash-table))
47 (loop for e in
(edges gr
) do
50 (when (and (null (gethash u matching
))
51 (null (gethash v matching
)))
52 (setf (gethash u matching
) v
)
53 (setf (gethash v matching
) u
))))
55 ;; augment the matching
56 (loop while
(not done
) do
60 ;; find unmatched vertices
62 (when (null (gethash u matching
))
65 (when (null (gethash u matching
))
68 ;; find augmenting path
69 (when (not (or (null au
)
71 (let ((prev (make-hash-table))
73 (loop while
(not (null new1
)) do
77 (loop for u in new1 do
78 (loop for v in
(neighbors u gr
) do
79 (when (and (null (gethash v prev
))
80 (or (null (gethash v matching
))
81 (not (= (gethash v matching
) u
))))
82 (setf (gethash v prev
) u
)
87 (loop for v in new2 do
88 (let ((u (gethash v matching
)))
91 (setf (gethash u prev
) v
)))))
93 ;; check for augmenting path
94 (loop for v in bu while
(null x
) do
95 (unless (null (gethash v prev
))
98 ;; extend the matching
101 (loop while
(not (null x
)) do
103 (v (gethash x prev
)))
104 (setf (gethash u matching
) v
)
105 (setf (gethash v matching
) u
)
106 (setq x
(gethash v prev
))))) )) ))
108 ;; we want the matching
109 (let ((mmatching ()))
110 (maphash #'(lambda (u v
) (if (< u v
)
111 (setq mmatching
(cons `((mlist simp
) ,u
,v
) mmatching
))))
113 (cons '(mlist simp
) mmatching
))
114 ;; we want the vertex cover
115 (get-cover-from-matching gr a matching
)) ))
117 (defun get-cover-from-matching (gr a matching
)
119 (loop for x in
(cdr a
) do
120 (when (null (gethash x matching
))
123 `((mlist simp
) ,@(sort (cdr a
) #'<))
125 ;; construct the Hungarian tree
126 (let ((prev (make-hash-table))
128 (loop while
(not (null new1
)) do
131 (loop for u in new1 do
132 (loop for v in
(neighbors u gr
) do
133 (when (and (null (gethash v prev
))
134 (or (null (gethash v matching
))
135 (not (= (gethash v matching
) u
))))
136 (setf (gethash v prev
) u
)
141 (loop for v in new2 do
142 (let ((u (gethash v matching
)))
145 (setf (gethash u prev
) v
)))))
146 (maphash #'(lambda (u v
)
148 (if (null (gethash v prev
))
152 `((mlist simp
) ,@(sort cov
#'<))))))
156 ;; set partition using hash-tales
159 (defun main-vertex (v sp
)
161 (loop while
(not (= m
(gethash m sp
))) do
162 (setq m
(gethash m sp
)))
163 (setf (gethash v sp
) m
)
166 (defun join-sets (v u sp
)
167 (setf (gethash (main-vertex u sp
) sp
)
170 (defun set-main-vertex (m v sp
)
171 (setf (gethash (main-vertex v sp
) sp
) m
)
172 (setf (gethash m sp
) m
))
179 (defvar *matching-state
*)
180 (defvar *matching-matching
*)
181 (defvar *matching-bridge
*)
182 (defvar *matching-prev
*)
183 (defvar *matching-path
*)
185 (defun maximum-matching-general (gr)
186 (let ((*matching-matching
* (make-hash-table))
190 (loop for e in
(edges gr
) do
193 (when (and (null (gethash u
*matching-matching
*))
194 (null (gethash v
*matching-matching
*)))
195 (setf (gethash u
*matching-matching
*) v
)
196 (setf (gethash v
*matching-matching
*) u
))))
198 ;; augment the matching
199 (loop while
(not done
) do
201 (let ((unmatched-vertices)
206 (*matching-bridge
* (make-hash-table))
208 (*matching-prev
* (make-hash-table))
211 ;; find unmatched vertices
212 (loop for v in
(vertices gr
) do
213 (when (null (gethash v
*matching-matching
*))
214 (push v unmatched-vertices
)))
216 (loop for first-vertex in unmatched-vertices while
(null x
) do
217 (setq dfsnum
(make-hash-table))
219 (setq *matching-state
* (make-hash-table))
220 (setq *matching-bridge
* (make-hash-table))
221 (setq vertex-partition
(make-hash-table))
222 (setq *matching-state
* (make-hash-table))
223 (setf (gethash first-vertex
*matching-state
*) 'A
)
224 (setq active-edges
())
226 (loop for v in
(vertices gr
) do
227 (setf (gethash v vertex-partition
) v
))
228 (loop for u in
(neighbors first-vertex gr
) do
229 (push (list first-vertex u
) active-edges
))
232 ;; find augmenting path
233 (loop while
(and (not (null active-edges
)) (null x
)) do
234 (let* ((e (pop active-edges
))
237 (v-main (main-vertex v vertex-partition
))
238 (w-main (main-vertex w vertex-partition
)))
240 (when (null (gethash v-main dfsnum
))
241 (setf (gethash v-main dfsnum
) dfsnum-curr
)
246 ;; we found an agugmented path
247 ((and (member w-main unmatched-vertices
)
248 (not (= w-main first-vertex
)))
249 (setf (gethash w-main
*matching-prev
*) v
)
252 ;; we didn't visit w yet
253 ((null (gethash w-main
*matching-state
*))
254 (setf (gethash w-main
*matching-state
*) 'B
)
255 (let ((z (gethash w-main
*matching-matching
*)))
256 (setf (gethash z
*matching-state
*) 'A
)
257 (loop for x in
(neighbors z gr
) do
259 (push (list z x
) active-edges
))))
260 (setf (gethash w-main
*matching-prev
*) v
))
263 ((and (not (= w-main v-main
)) ;; in the same blossom already
264 (eql (gethash w-main
*matching-state
*) 'A
))
266 (let ((fst) (lst) (b))
267 (if (> (gethash w-main dfsnum
)
268 (gethash v-main dfsnum
))
277 (loop while
(not (= tmp-vrt lst
)) do
278 (setf (gethash tmp-vrt
*matching-bridge
*) b
)
279 (join-sets lst tmp-vrt vertex-partition
)
280 (if (eql (gethash tmp-vrt
*matching-state
*) 'A
)
283 (setq tmp-vrt
(main-vertex (gethash tmp-vrt
*matching-matching
*)
285 (loop for u in
(neighbors tmp-vrt gr
) do
286 (setq u
(main-vertex u vertex-partition
))
287 (push (list tmp-vrt u
) active-edges
)))
290 (setq tmp-vrt
(main-vertex (gethash tmp-vrt
*matching-prev
*)
291 vertex-partition
))))))) )
297 (let ((*matching-path
* (list x
)))
298 (find-augmenting-path first-vertex
(gethash x
*matching-prev
*))
299 (loop while
*matching-path
* do
300 (let ((a (car *matching-path
*))
301 (b (cadr *matching-path
*)))
302 (setf (gethash a
*matching-matching
*) b
)
303 (setf (gethash b
*matching-matching
*) a
)
304 (setf *matching-path
* (cddr *matching-path
*))))
309 (maphash #'(lambda (u v
) (if (< u v
) (setq matching
(cons `((mlist simp
) ,u
,v
) matching
))))
311 (cons '(mlist simp
) matching
))))
313 ;; returns the path from b to a
314 (defun find-augmenting-path (a b
)
317 (push a
*matching-path
*))
319 ((eql (gethash b
*matching-state
*) 'A
)
320 (push b
*matching-path
*)
321 (push (gethash b
*matching-matching
*) *matching-path
*)
322 (find-augmenting-path a
(gethash (gethash b
*matching-matching
*) *matching-prev
*)))
325 (push b
*matching-path
*)
326 (find-augmenting-path-1 (gethash b
*matching-matching
*) (first (gethash b
*matching-bridge
*)))
327 (find-augmenting-path a
(second (gethash b
*matching-bridge
*)))) ))
329 (defun find-augmenting-path-1 (a b
)
332 (push a
*matching-path
*))
334 ((eql (gethash b
*matching-state
*) 'A
)
335 (find-augmenting-path-1 a
(gethash (gethash b
*matching-matching
*) *matching-prev
*))
336 (push (gethash b
*matching-matching
*) *matching-path
*)
337 (push b
*matching-path
*))
340 (find-augmenting-path-1 a
(second (gethash b
*matching-bridge
*)))
341 (find-augmenting-path (gethash b
*matching-matching
*) (first (gethash b
*matching-bridge
*)))
342 (push b
*matching-path
*)) ))