SMAS 2.0bA Primer |
5. LISP Code for
the Examples
(Fast
FACTORIAL)
The DUMB-FACTORIAL skill is really dumb. A better approach would be to send a broadcast message to all (unknown) agents for granting contracts and reusing the same mechanism in the dynamic part. Thus, if there are several agents knowing how to multiply two numbers together, we can achieve a degree of parallelism. Of course this assumes that in practice the agents are located on different machines.
The FAST-FACTORIAL skill implements this scheme.
(defskill fast-factorial FACTORIAL :static-fcn static-fast-factorial :dynamic-fcn dynamic-fast-factorial)
(defun static-fast-factorial (agent environment nn) "this skill works by first creating n/2 tasks for distribution to the ~ multiplying agents, i.e., (* n n-1) (* n-2 n-3) ... if n is even we end up with (* 2 1) if n is odd we end up with (° 3 2) It then broadcasts those tasks, record the left over number (1 or 2) and quits." (let ((*default-call-for-bids-timeout-delay* 3)) ;; we want to control the calll for bid delay for the contract net to prevent early ;; aborts (if (< nn 2) (static-exit agent 1) ;; this line should be replaced with (if (< nn 3) (return (max nn 1)) ;; to avoid subcontracting (* 2 1) (let (env (delay 0)) ;; try to produce as many initial subtasks as possible (loop ;; create n/2 tasks for distribution to the multiplying agents ;; create a subtask for computing the product of the next two values ;; broadcast (send-subtask agent :to 'ALL :action 'multiply :args (list nn (decf nn)) :delay delay :protocol :contract-net) (decf nn) ; adjust nn for next round (incf *default-call-for-bids-timeout-delay* 3) ; increase waiting time ;;... to let agents make their bids (incf delay) ; we assume 1 as cost of setting up subtask ;; if nn becomes less than 3 don't multiply 2 by 1! get out of the loop (if (< nn 3)(return nil))) ;; define the :n tag in the environment to record the value of the next ;; products to compute. e.g., nn-1 -> (nn - 1)! (setq env (append (list :res nn) environment)) ;; record environment (update-environment agent env) ;; quit (static-exit agent :done)))))
Note the 2 additional optional arguments in the send-subtask function:
SMAS knows only two protocols: simple-protocol (default) and contract-net. They are controled at the simulator level, thus all agents can use them. Note that protocols can be mixed.
(defun dynamic-fast-factorial (agent environment answer) "this function is called whenever we get a result from a subtask. We structured the environment as follow: :res contains a partial result (nil or 2 at the beginning) We use :res as follows: if 1 we save the result into it otherwise, we broadcast the multiplication of :res with the result." (let (res) ;; we are finished when there are no more active substasks. (cond ((not (pending-subtasks? agent)) ;; thus we do a final exit (dynamic-exit agent answer)) ((eql 1 (setq res (cadr (member :res environment)))) ;; we have still subtasks in progress and res is nil ;; store the answer (setf (cadr (member :res environment)) answer) (update-environment agent environment) ;; check if this was the last subtask in the list (if (null (cdr (pending-subtasks? agent))) (return-from dynamic-fast-factorial (dynamic-exit agent answer))) ;; otherwise return answer answer) (t ;; res is not 1, i.e., it contains a partial result ;; we multiply the answer with res creating a subtask ;; ... broadcasting it (send-subtask agent :to 'ALL :action 'multiply :args (list answer res) :protocol :contract-net) ;; update environment (setf (cadr (member :res environment)) 1) (update-environment agent environment) answer))))
Copyrignt Jean-Paul Barthès@UTC, 1998 Last update, Jan 99