dftk
[ class tree: dftk ] [ index: dftk ] [ all elements ]

Source for file dftk_ai_idastar.php

Documentation is available at dftk_ai_idastar.php


1 <?php
2 /*
3
4 Copyright (c)2003 DuckCorp(tm) and RtpNet(tm)
5
6
7
8 This file is part of DFTK
9
10 DFTK is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 DFTK is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with DFTK if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25
26 /**
27 * @package dftk
28 * @author Duck <duck@DuckCorp.org>
29 * @author Rtp <rtp@rtp-net.org>
30 * @copyright Copyright (c)2003 DuckCorp(tm) and RtpNet(tm)
31 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
32 * @version 0.4.0
33 */
34
35
36 /**
37 * DFTK IDA* algorithm
38 *
39 * @package dftk
40 * @subpackage dftk-ai
41 * @author Duck <duck@DuckCorp.org>
42 *
43 * @access public
44 */
45
46 class DftkAiIDAstar
47 {
48 /**
49 * Trace Manager
50 *
51 * @access private
52 * @var string
53 */
54 var $_tracemgr;
55
56 /**
57 * Node list
58 *
59 * @access private
60 * @var array
61 */
62 var $_node_list;
63
64 /**
65 * Cost function name
66 *
67 * @access private
68 * @var string
69 */
70 var $_cost_fct;
71
72 /**
73 * Heuristic function name
74 *
75 * @access private
76 * @var string
77 */
78 var $_heuristic_fct;
79
80 /**
81 * Successor selection function name
82 *
83 * @access private
84 * @var string
85 */
86 var $_successor_selection_fct;
87
88 /**
89 * Start node
90 *
91 * @access private
92 * @var string
93 */
94 var $_start_node;
95
96 /**
97 * Stop node
98 *
99 * @access private
100 * @var string
101 */
102 var $_stop_node;
103
104 /**
105 * Routes cache
106 *
107 * @access private
108 * @var array
109 */
110 var $_routes_cache;
111
112 /**
113 * Constructor
114 *
115 * @access public
116 * @param object DftkDaTraceManager &$tracemgr Language Manager
117 */
118 function DftkAiIDAstar(&$tracemgr)
119 {
120 $this->_tracemgr =& $tracemgr;
121
122 if (!$this->_tracemgr->is_module("DFTK-LDAP"))
123 $this->_tracemgr->register_traces("DFTK-IA",DFTK_ROOT_PATH."/ai/traces/");
124
125 register_shutdown_function(array(&$this, "_DftkAiIDAstar"));
126
127 $this->_node_list = array();
128 }
129
130 /**
131 * Destructor
132 *
133 * @access private
134 */
135 function _DftkAiIDAstar()
136 {
137 }
138
139 /**
140 * Set node list
141 *
142 * The node list contains the one-way roads to other nodes;
143 * to use both-way roads you have to specify two one-way roads.
144 *
145 * Node list is indexed by <node_name> and contains these fields :
146 * + 'successors' : array
147 * + 'data' : optional user field
148 *
149 * Each element of the 'successors' array is a couple (array) :
150 * + <next_node_name>
151 * + <road_name>
152 *
153 * The road name is only informational.
154 *
155 * The 'data' field can contain whatever data the user need to compute
156 * cost and heuritic values (eg. geographic position).
157 *
158 * @access public
159 * @param array $node_list Node list
160 * @return object DftkDaTrace $r Trace
161 */
162 function &set_node_list($node_list)
163 {
164 $r =& $this->_tracemgr->create_trace();
165
166 $this->_node_list = $node_list;
167 $this->_routes_cache = array();
168
169 return $r;
170 }
171
172 /*
173 * Set the cost function name
174 *
175 * This function must accept the following parameters :
176 * + node_list
177 * + node
178 * + next_node
179 *
180 * It must return the cost to go from node to next_node.
181 *
182 * @access public
183 * @param array $node_list Node list
184 * @return object DftkDaTrace $r Trace
185 */
186 function &set_cost_fct($cost_fct)
187 {
188 $r =& $this->_tracemgr->create_trace();
189
190 $this->_cost_fct = $cost_fct;
191 $this->_routes_cache = array();
192
193 return $r;
194 }
195
196 /*
197 * Set the heuristic function name
198 *
199 * This function must accept the following parameters :
200 * + node_list
201 * + start_node
202 * + stop_node
203 * + node
204 * + next_node
205 *
206 * It must return an heuristic value assigned to the selected road.
207 *
208 * The following extra field is available in the node_list :
209 * + father : the previous node
210 *
211 * @access public
212 * @param array $node_list Node list
213 * @return object DftkDaTrace $r Trace
214 */
215 function &set_heuristic_fct($heuristic_fct)
216 {
217 $r =& $this->_tracemgr->create_trace();
218
219 $this->_heuristic_fct = $heuristic_fct;
220 $this->_routes_cache = array();
221
222 return $r;
223 }
224
225 /*
226 * Set the successor selection function name
227 *
228 * This function must accept the following parameters :
229 * + node_list
230 * + successor_list
231 * + node
232 *
233 * It must choose one of the possible successors and return its index number.
234 * If the number returned is out of range, the first one in the list is used.
235 *
236 * If not specified the default is to take the first one in the list.
237 *
238 * @access public
239 * @param array $node_list Node list
240 * @return object DftkDaTrace $r Trace
241 */
242 function &set_successor_selection_fct($successor_selection_fct)
243 {
244 $r =& $this->_tracemgr->create_trace();
245
246 $this->_successor_selection_fct = $successor_selection_fct;
247 $this->_routes_cache = array();
248
249 return $r;
250 }
251
252 /**
253 * Initialize private information in the node list
254 *
255 * @access private
256 */
257 function _init()
258 {
259 foreach ($this->_node_list as $key => $v)
260 {
261 $this->_node_list[$key]['father'] = null;
262 $this->_node_list[$key]['f'] = 0;
263 $this->_node_list[$key]['g'] = 0;
264 }
265 }
266
267 /**
268 * Get the path to go from one node to another
269 *
270 * @access public
271 * @param string $start_node Start node
272 * @param string $stop_node Stop node
273 * @return object DftkDaTrace $r Trace
274 */
275 function &get_path($start_node, $stop_node)
276 {
277 $r =& $this->_tracemgr->create_trace();
278
279 if (count($this->_node_list)==0)
280 $r->add_event('dftk-ai_missnodelist');
281 if ($this->_cost_fct=="")
282 $r->add_event('dftk-ai_misscostfct');
283 if ($this->_heuristic_fct=="")
284 $r->add_event('dftk-ai_missheuristicfct');
285
286 if (!array_key_exists($start_node, $this->_node_list))
287 $r->add_event('dftk-ai_badstartnode', $start_node);
288 if (!array_key_exists($stop_node, $this->_node_list))
289 $r->add_event('dftk-ai_badstopnode', $stop_node);
290
291 if (!$r->has_error())
292 {
293 $this->_start_node = $start_node;
294 $this->_stop_node = $stop_node;
295
296 if ($this->_routes_cache[$this->_start_node][$this->_stop_node])
297 $r->set_result('path', $this->_routes_cache[$this->_start_node][$this->_stop_node]);
298 else if ($this->_routes_cache[$this->_stop_node][$this->_start_node])
299 $r->set_result('path', array_reverse($this->_routes_cache[$this->_stop_node][$this->_start_node]));
300 else
301 {
302 $this->_routes_cache[$this->_start_node][$this->_stop_node] = $this->_find_path($this->_node_list, $this->_start_node, $this->_stop_node);
303 $r->set_result('path', $this->_routes_cache[$this->_start_node][$this->_stop_node]);
304 }
305 }
306
307 return $r;
308 }
309
310 /**
311 * Compute one of the best path to go from one node to another
312 *
313 * @access private
314 * @return array $r Path
315 */
316 function _find_path()
317 {
318 $this->_init();
319
320 $P = array($this->_start_node);
321 $Q = array();
322 $node = $this->_start_node;
323 $cost = $this->_cost_fct;
324 $heuristic = $this->_heuristic_fct;
325
326 while ((count($P)!=0) && ($node!=$this->_stop_node))
327 {
328 $v = $this->_select_successor(array_merge($P, $Q), $node);
329 if (!$v)
330 {
331 //print("finished with node : ".$node."\n");
332 $P = dftk_array_del_value($P, $node);
333 //print("P : ");
334 //print_r($P);
335 array_push($Q, $node);
336 //print("Q : ");
337 //print_r($Q);
338 }
339 else
340 {
341 $next_node = $v[0];
342 //print("current node : ".$node."\n");
343 //print("next node : ".$next_node."\n");
344 $this->_node_list[$next_node][g] = $this->_node_list[$node][g]+$cost($this->_node_list, $node, $next_node);
345 $this->_node_list[$next_node][f] = $this->_node_list[$next_node][g] + $heuristic($this->_node_list, $this->_start_node, $this->_stop_node, $node, $next_node);
346 $this->_node_list[$next_node][father] = array($node, $v[1]);
347 $P = $this->_array_sorted_insert($P, $next_node);
348 //print("P : ");
349 //print_r($P);
350 }
351 if (count($P)!=0)
352 $node = $P[0];
353 }
354
355 if (count($P)==0)
356 return null;
357 return $this->_path($node);
358 }
359
360 /**
361 * Search the possible successors for the current node, and choose one
362 *
363 * @access private
364 * @param array $union Nodes not to explore
365 * @param string $node Current node name
366 * @return string $r Name of the choosen node
367 */
368 function _select_successor($union, $node)
369 {
370 $S = array();
371 $cost = $this->_cost_fct;
372
373 if (count($this->_node_list[$node][successors])>0)
374 foreach ($this->_node_list[$node][successors] as $v)
375 {
376 //print ("possible successor : ".$v[0]." g(v)=".$this->_node_list[$v[0]][g]." g(u)=".$this->_node_list[$node][g]." cost=".$cost($this->_node_list, $node, $v[0])."\n");
377 if ((!in_array($v[0], $union)) || ($this->_node_list[$v[0]][g] > $this->_node_list[$node][g]+$cost($this->_node_list, $node, $v[0])))
378 $S[] = $v;
379 }
380
381 if (count($S) == 0)
382 $r = null;
383 elseif ($this->_successor_selection_fct!="")
384 {
385 $successor_selection_fct = $this->_successor_selection_fct;
386 $i = $successor_selection_fct($this->_node_list, $S, $node);
387 if (($i<0) || ($i>=count($S)))
388 $i = 0;
389 $r = $S[$i];
390 }
391 else
392 $r = $S[0];
393
394 // choix du successeur
395 //print("successeur : ");
396 //print_r($r);
397 return $r;
398 }
399
400 /**
401 * Insert a value in a sorted array
402 *
403 * This function ensure the array will stay sorted after each insert.
404 *
405 * @internal Cannot use dftk_array_sorted_insert as the sort function is in fact a non-static method
406 *
407 * @access private
408 * @param array $tab_orig Array to process
409 * @param mixed $value Value to insert
410 * @return array $tab Array processed
411 */
412 function _array_sorted_insert($tab_orig, $value)
413 {
414 $n = count($tab_orig);
415
416 for ($i=0; $i<count($tab_orig); $i++)
417 if ($this->_test_func($value, $tab_orig[$i]))
418 {
419 $n = $i;
420 break;
421 }
422
423 return dftk_array_insert($tab_orig, $n, $value);
424 }
425
426 /**
427 * Sort function for P
428 *
429 * @access private
430 * @param string $state1 Node 1
431 * @param string $state2 Node 2
432 * @return boolean $r true if node 1 comes before node 2
433 */
434 function _test_func($state1, $state2)
435 {
436 $f1 = $this->_node_list[$state1][f];
437 $f2 = $this->_node_list[$state2][f];
438
439 if ($f1 < $f2)
440 return true;
441 if ($f1 > $f2)
442 return false;
443
444 $g1 = $this->_node_list[$state1][g];
445 $g2 = $this->_node_list[$state2][g];
446
447 if ($g1 > $g2)
448 return true;
449 if ($g1 < $g2)
450 return false;
451
452 if ($state1 == $this->_stop_node)
453 return true;
454
455 return false;
456 }
457
458 /**
459 * Retrieve the path found to the specified node
460 *
461 * The path is an array of couples (<node_name>, <road_name>) to follow
462 * from the start_node to reach the stop_node..
463 *
464 * @access private
465 * @param string $node Node
466 * @return array $r Path
467 */
468 function _path($node)
469 {
470 $father = $this->_node_list[$node][father];
471 if ($father)
472 return array_merge($this->_path($father[0]), array(array($node, $father[1])));
473 else
474 return array();
475 }
476
477 }
478
479 ?>

Documentation generated on Sat, 6 Dec 2003 13:47:33 +0100 by phpDocumentor 1.2.3