Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests that flex dryad expansion #54

Merged
merged 7 commits into from
Dec 9, 2023
Merged
1 change: 1 addition & 0 deletions anatevka-tests.asd
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
:components ((:file "package")
(:file "suite")
(:file "node")
(:file "dryad")
(:module "operations"
:serial t
:components ((:file "graft")
Expand Down
8 changes: 8 additions & 0 deletions src/dryad.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
:initform 'blossom-node
:type symbol
:documentation "The class identifier for nodes that this `DRYAD' works with. Can be a `BLOSSOM-NODE' or any subclass of `BLOSSOM-NODE'.")
(shuffle?
:accessor dryad-shuffle?
:initarg :shuffle?
:initform nil
:type boolean
:documentation "If T, the list of channels to try is shuffled before a `MESSAGE-DISCOVERY' is sent to the querying node.")
;; local state
(ids
:accessor dryad-ids
Expand Down Expand Up @@ -70,6 +76,8 @@ NOTE: In the basic implementation, these messages must be waiting for the DRYAD
(loop :for address :being :the :hash-keys :of (dryad-ids dryad)
:unless (address= address (message-discover-address message))
:collect address)))
(when (dryad-shuffle? dryad)
(setf channels (a:shuffle channels)))
(send-message (message-reply-channel message)
(make-message-discovery :channels-to-try channels))))

Expand Down
3 changes: 2 additions & 1 deletion src/operations/multireweight.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ Then, we reach the \"critical segment\", where it becomes impossible to rewind p
(define-process-upkeep ((supervisor supervisor) now)
(CHECK-PRIORITY source-root target-roots)
"Confirm that, of the roots in the hold cluster, we have priority to act. Namely, we have priority when our `SOURCE-ROOT' carries the minimum ID (i.e. coordinate) of all the roots in the `hold-cluster' (passed as `TARGET-ROOTS')."
(let ((hold-cluster target-roots))
;; `target-roots' includes `source-root', so we begin by removing it
(let ((hold-cluster (remove source-root target-roots :test #'address=)))
(sync-rpc (make-message-id-query) (source-id source-root)
(with-replies (replies)
(send-message-batch #'make-message-id-query hold-cluster)
Expand Down
1 change: 1 addition & 0 deletions src/package.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#:dryad-match-address ; ACCESSOR
#:dryad-ids ; ACCESSOR
#:dryad-sprouted? ; ACCESSOR
#:dryad-shuffle? ; ACCESSOR
#:vertex-vertex-distance ; GENERIC FUNCTION

;; messages
Expand Down
4 changes: 3 additions & 1 deletion tests/blossom.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ NOTE: This macro automatically rescales the pairs in `COORDINATES' to reside at
(dryad (spawn-process ',dryad-class
:process-clock-rate ,dryad-clock-rate
:match-address channel
:debug? ,debug?)))
:debug? ,debug?
:shuffle? t))
(*random-state* (sb-kernel::seed-random-state i)))
(with-simulation (simulation (*local-courier* dryad))
(anatevka::reset-logger)
(when (= 0 (mod i 50))
Expand Down
227 changes: 227 additions & 0 deletions tests/dryad.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
;;;; tests/dryad.lisp
;;;;
;;;; Tests dryad functionality: in particular the expansion of macrovertices at
;;;; the termination of the blossom algorithm.

(in-package #:anatevka-tests)

(deftest test-dryad-expand-barbell-3-blossom ()
"Tests that a DRYAD will properly execute the following blossom expansion:

+
BLOSSOM + + + + +
[A --- B --- C] === D --> A === B C === D
[\-----------/]
"
(with-with ((with-courier ())
(with-simulation (simulation (*local-courier*))))
(let* ((dryad (spawn-process 'dryad :match-address (register)
:debug? t))
(dryad-address (process-public-address dryad)))
(simulation-add-event simulation (make-event :callback dryad))
(blossom-let (original-tree :dryad dryad-address)
((BLOSSOM :id (gensym "BLOSSOM")
:match-edge (bb-edge BLOSSOM C D D)
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A)))
(A :id (id 0) :internal-weight 2 :pistil BLOSSOM)
(B :id (id 2) :pistil BLOSSOM)
(C :id (id 4) :internal-weight 2 :pistil BLOSSOM)
(D :id (id 6) :match-edge (bb-edge D D C BLOSSOM)))
(simulate-add-tree simulation original-tree :dryad dryad)
(simulate-until-dead simulation BLOSSOM :timeout 100)
(blossom-let (target-tree :dryad dryad-address)
((BLOSSOM-new :id (slot-value BLOSSOM 'anatevka::id)
:match-edge (bb-edge BLOSSOM-new C D D)
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A))
:wilting t)
(A :id (id 0) :match-edge (vv-edge A B)
:internal-weight 2)
(B :id (id 2) :match-edge (vv-edge B A))
(C :id (id 4) :match-edge (vv-edge C D)
:internal-weight 2)
(D :id (id 6) :match-edge (vv-edge D C)))
(is (tree-equalp original-tree target-tree)))))))

(deftest test-dryad-expand-barbell-3-blossom-x2 ()
"Tests that a DRYAD will properly execute the following blossom expansion:

+ +
BLOSSOM BLOSSOM2 + + + + + +
[A --- B --- C] === [D --- E --- F] --> A === B C === D E === F
[\-----------/] [\-----------/]
"
(with-with ((with-courier ())
(with-simulation (simulation (*local-courier*))))
(let* ((dryad (spawn-process 'dryad :match-address (register)
:debug? t))
(dryad-address (process-public-address dryad)))
(simulation-add-event simulation (make-event :callback dryad))
(blossom-let (original-tree :dryad dryad-address)
((BLOSSOM :id (gensym "BLOSSOM-A")
:match-edge (bb-edge BLOSSOM C D BLOSSOM2)
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A)))
(BLOSSOM2 :id (gensym "BLOSSOM-B")
:match-edge (bb-edge BLOSSOM2 D C BLOSSOM)
:petals (list (vv-edge D F)
(vv-edge F E)
(vv-edge E D)))
(A :id (id 0) :internal-weight 2 :pistil BLOSSOM)
(B :id (id 2) :pistil BLOSSOM)
(C :id (id 4) :internal-weight 2 :pistil BLOSSOM)
(D :id (id 6) :pistil BLOSSOM2)
(E :id (id 8) :internal-weight 2 :pistil BLOSSOM2)
(F :id (id 10) :pistil BLOSSOM2)
)
(simulate-add-tree simulation original-tree :dryad dryad)
(simulate-until-dead simulation BLOSSOM :timeout 100)
(simulate-until-dead simulation BLOSSOM2 :timeout 100)
(blossom-let (target-tree :dryad dryad-address)
((BLOSSOM-new :id (slot-value BLOSSOM 'anatevka::id)
:match-edge (bb-edge BLOSSOM-new C D D)
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A))
:wilting t)
;; nb: the match-edge here still has the other macrovertex in it
;; which probably means that BLOSSOM2 was popped first
(BLOSSOM2-new :id (slot-value BLOSSOM2 'anatevka::id)
:match-edge (bb-edge BLOSSOM2-new D C BLOSSOM-new)
:petals (list (vv-edge D F)
(vv-edge F E)
(vv-edge E D))
:wilting t
)
(A :id (id 0) :match-edge (vv-edge A B)
:internal-weight 2)
(B :id (id 2) :match-edge (vv-edge B A))
(C :id (id 4) :match-edge (vv-edge C D)
:internal-weight 2)
(D :id (id 6) :match-edge (vv-edge D C))
(E :id (id 8) :internal-weight 2 :match-edge (vv-edge E F))
(F :id (id 10) :match-edge (vv-edge F E))
)
(is (tree-equalp original-tree target-tree)))))))

(deftest test-dryad-expand-barbell-3-blossom-nested ()
"Tests that a DRYAD will properly execute the following blossom expansion:

BLOSSOM
[[A --- B --- C] --- D --- E] === F --> A === B C === D E === F
[\-----------/]
[\--------------------------/]
BLOSSOM2
"
(with-with ((with-courier ())
(with-simulation (simulation (*local-courier*))))
(let* ((dryad (spawn-process 'dryad :match-address (register)
:debug? t))
(dryad-address (process-public-address dryad)))
(simulation-add-event simulation (make-event :callback dryad))
(blossom-let (original-tree :dryad dryad-address)
((BLOSSOM :id (gensym "BLOSSOM-A")
:pistil BLOSSOM2
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A)))
(BLOSSOM2 :id (gensym "BLOSSOM-B")
:match-edge (bb-edge BLOSSOM2 E F F)
:petals (list (bb-edge BLOSSOM C E E)
(vv-edge E D)
(bb-edge D D C BLOSSOM)))
(A :id (id 0) :internal-weight 2 :pistil BLOSSOM)
(B :id (id 2) :pistil BLOSSOM)
(C :id (id 4) :internal-weight 2 :pistil BLOSSOM)
(D :id (id 6) :pistil BLOSSOM2)
(E :id (id 8) :internal-weight 2 :pistil BLOSSOM2)
(F :id (id 10)
:match-edge (bb-edge F F E BLOSSOM2)
)
)
(simulate-add-tree simulation original-tree :dryad dryad)
(simulate-until-dead simulation BLOSSOM2 :timeout 100)
(simulate-until-dead simulation BLOSSOM :timeout 100)
(blossom-let (target-tree :dryad dryad-address)
((BLOSSOM-new :id (slot-value BLOSSOM 'anatevka::id)
:match-edge (bb-edge BLOSSOM-new C D D)
:petals (list (vv-edge A C)
(vv-edge C B)
(vv-edge B A))
:wilting t)
(BLOSSOM2-new :id (slot-value BLOSSOM2 'anatevka::id)
:match-edge (bb-edge BLOSSOM2-new E F F)
:petals (list (bb-edge BLOSSOM-new C E E)
(vv-edge E D)
(bb-edge D D C BLOSSOM-new))
:wilting t
)
(A :id (id 0) :match-edge (vv-edge A B)
:internal-weight 2)
(B :id (id 2) :match-edge (vv-edge B A))
(C :id (id 4) :match-edge (vv-edge C D)
:internal-weight 2)
(D :id (id 6) :match-edge (vv-edge D C))
(E :id (id 8) :internal-weight 2 :match-edge (vv-edge E F))
(F :id (id 10) :match-edge (vv-edge F E))
)
(is (tree-equalp original-tree target-tree)))))))

(deftest test-dryad-expand-barbell-5-blossom ()
"Tests that a DRYAD will properly execute the following blossom expansion:

+
BLOSSOM + +
[B --- C] B === C
[| |]
[| |]
[A |] --> A +
[| |] !
[| |] ! + +
[D --- E] === F + D E === F
"
(with-with ((with-courier ())
(with-simulation (simulation (*local-courier*))))
(let* ((dryad (spawn-process 'dryad :match-address (register)
:debug? t))
(dryad-address (process-public-address dryad)))
(simulation-add-event simulation (make-event :callback dryad))
(blossom-let (original-tree :dryad dryad-address)
((BLOSSOM :id (gensym "BLOSSOM")
:match-edge (bb-edge BLOSSOM E F F)
:petals (list (vv-edge A D)
(vv-edge D E)
(vv-edge E C)
(vv-edge C B)
(vv-edge B A)))
(A :id (id 0 2) :internal-weight 2 :pistil BLOSSOM)
(B :id (id 0 4) :pistil BLOSSOM)
(C :id (id 2 4) :internal-weight 2 :pistil BLOSSOM)
(D :id (id 0 0) :pistil BLOSSOM)
(E :id (id 2 0) :internal-weight 2 :pistil BLOSSOM)
(F :id (id 4 0) :match-edge (bb-edge F F E BLOSSOM)))
(simulate-add-tree simulation original-tree :dryad dryad)
(simulate-until-dead simulation BLOSSOM :timeout 100)
(blossom-let (target-tree :dryad dryad-address)
((BLOSSOM-new :id (slot-value BLOSSOM 'anatevka::id)
:match-edge (bb-edge BLOSSOM-new E F F)
:petals (list (vv-edge A D)
(vv-edge D E)
(vv-edge E C)
(vv-edge C B)
(vv-edge B A))
:wilting t)
(A :id (id 0 2) :match-edge (vv-edge A D)
:internal-weight 2)
(B :id (id 0 4) :match-edge (vv-edge B C))
(C :id (id 2 4) :match-edge (vv-edge C B)
:internal-weight 2)
(D :id (id 0 0) :match-edge (vv-edge D A))
(E :id (id 2 0) :match-edge (vv-edge E F)
:internal-weight 2)
(F :id (id 4 0) :match-edge (vv-edge F E)))
(is (tree-equalp original-tree target-tree)))))))
12 changes: 9 additions & 3 deletions tests/node.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,16 @@ Finally, all of the nodes constructed by this BLOSSOM-LET are stashed in the pla
'(list '(anatevka::IDLE)))))
,@body)))))

(defun simulate-add-tree (simulation tree &key (start-time 0))
"BLOSSOM-LET binds a place that knows about all the nodes it's constructed, called the \"tree\". SIMULATE-ADD-TREE takes the contents of this place and adds it to a SIMULATION object."
(defun simulate-add-tree (simulation tree &key (start-time 0) dryad (sprouted? t))
"BLOSSOM-LET binds a place that knows about all the nodes it's constructed, called the \"tree\". SIMULATE-ADD-TREE takes the contents of this place and adds it to a SIMULATION object. If a `DRYAD' is provided, we set its slots for each node."
(dolist (node tree)
(simulation-add-event simulation (make-event :callback node :time start-time))))
(simulation-add-event simulation (make-event :callback node :time start-time))
(when dryad
(unless (anatevka::blossom-node-petals node)
(let ((id (slot-value node 'anatevka::id))
(address (process-public-address node)))
(setf (gethash address (dryad-ids dryad)) id
(gethash address (dryad-sprouted? dryad)) sprouted?))))))

(defun simulate-until-dead (simulation process &key (start-time 0) timeout)
"Runs SIMULATION until PROCESS exhausts its command queue."
Expand Down