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 ?>
|