Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
explain.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: explain.cpp
11  *
12  * =======================================================================
13  * Description : To provide a function which at the least can do :
14  * (explain chunk-1)
15  * lists conditions -- given one can list the productions
16  * which fired & caused it to be in the chunk.
17  *
18  * (It would only run AFTER backtracing).
19  * =======================================================================
20  */
21 
22 
23 /*
24  NOTES :
25  1) Explain search just finds ANY path--should be shortest.
26 */
27 
28 #include <stdlib.h>
29 
30 #include "explain.h"
31 #include "kernel.h"
32 #include "agent.h"
33 #include "backtrace.h"
34 #include "production.h"
35 #include "gdatastructs.h"
36 #include "print.h"
37 
38 /* Define the "local" globals if that makes sense.
39  (Only accessed in this file)
40  The backtrace_list is built up until a call is made to create a new
41  entry in the chunk_list. At that time the current backtrace list is
42  included in that structure and a new backtrace list begun. */
43 
44 /* static explain_chunk_str *explain_chunk_list;
45  static backtrace_str *explain_backtrace_list;
46  static char explain_chunk_name[256] = { '\0' };
47  AGR 564 */
48 
49 /* AGR 564 This bug report came complete with fixes from Frank Koss.
50  So, I just implemented the fixes. The files with changes in them
51  for this bug are explain.c, explain.h,
52  init_soar.c, interface.c, and soarkernel.h. AGR 564 2-May-94 */
53 
54 /***************************************************************************
55  * Function : init_explain
56  **************************************************************************/
57 
58 void init_explain (agent* thisAgent) {
59 
60 /* "AGR 564" applies to this whole function */
61 
62  thisAgent->explain_chunk_name[0] = '\0';
63  thisAgent->explain_chunk_list = NULL;
64  thisAgent->explain_backtrace_list = NULL;
65  /* added in this initialization, not sure why removed... KJC 7/96 */
66  /* thisAgent->explain_flag = FALSE;
67  */
68  /* should we be re-initializing here?? */
69  set_sysparam (thisAgent, EXPLAIN_SYSPARAM,FALSE);
70 
71 /*
72  * add_help("explain",help_on_explain);
73  * add_command("explain",explain_interface_routine);
74  *
75  * explain_chunk_list = NULL;
76  * explain_backtrace_list = NULL;
77  * thisAgent->explain_flag = FALSE;
78  */
79 }
80 
81 /***************************************************************************
82  * Function : free_backtrace_list
83  **************************************************************************/
84 
85 void free_backtrace_list(agent* thisAgent, backtrace_str *prod) {
86 
87 backtrace_str *next_prod;
88 
89  while (prod != NULL) {
90  next_prod = prod->next_backtrace;
91  deallocate_condition_list(thisAgent, prod->trace_cond);
92  deallocate_condition_list(thisAgent, prod->grounds);
93  deallocate_condition_list(thisAgent, prod->potentials);
94  deallocate_condition_list(thisAgent, prod->locals);
95  deallocate_condition_list(thisAgent, prod->negated);
96  free(prod);
97  prod = next_prod;
98  }
99 }
100 
101 /***************************************************************************
102  * Function : reset_backtrace_list
103  **************************************************************************/
104 
105 void reset_backtrace_list (agent* thisAgent) {
106 
107  free_backtrace_list(thisAgent, thisAgent->explain_backtrace_list);
108  thisAgent->explain_backtrace_list = NULL;
109 /* AGR 564 In both statements, the current_agent(...) was added. 2-May-94 */
110 
111 }
112 
113 /***************************************************************************
114  * Function : copy_cond_list
115  **************************************************************************/
116 
117 condition * copy_cond_list(agent* thisAgent, condition *top_list) {
118 
119  condition *new_top = 0;
120  condition *new_bottom = 0;
121 
122  copy_condition_list(thisAgent, top_list,&new_top,&new_bottom);
123  return (new_top);
124 }
125 
126 /***************************************************************************
127  * Function : copy_conds_from_list
128  **************************************************************************/
129 
130 condition *copy_conds_from_list(agent* thisAgent, cons *top_list) {
131 
132 condition *top,*cond,*prev,*next;
133 cons *cc;
134 
135  prev = next = top = NIL;
136 
137  for (cc=top_list; cc!=NIL; cc=cc->rest) {
138  cond = copy_condition(thisAgent, static_cast<condition_struct *>(cc->first));
139  cond->prev = prev;
140  cond->next = NIL;
141 
142  if (prev == NIL)
143  top = cond;
144  else
145  prev->next = cond;
146 
147  prev = cond;
148  }
149  return (top);
150 }
151 
152 /***************************************************************************
153  * Function : explain_add_temp_to_backtrace_list
154  **************************************************************************/
155 
157  (agent* thisAgent, backtrace_str *temp, cons *grounds, cons *pots, cons *locals, cons *negateds) {
158 
159 backtrace_str *back;
160 
161  back = static_cast<backtrace_str *>(malloc(sizeof (backtrace_str)));
162  back->result = temp->result;
163  back->trace_cond = copy_condition(thisAgent, temp->trace_cond);
164  if (back->trace_cond != NULL)
165  back->trace_cond->next = NULL;
166  strncpy(back->prod_name,temp->prod_name, BUFFER_PROD_NAME_SIZE);
167  back->prod_name[BUFFER_PROD_NAME_SIZE - 1] = 0; /* ensure null termination */
168 
169  back->grounds = copy_conds_from_list(thisAgent, grounds);
170  back->potentials = copy_conds_from_list(thisAgent, pots);
171  back->locals = copy_conds_from_list(thisAgent, locals);
172  back->negated = copy_conds_from_list(thisAgent, negateds);
173 
174  back->next_backtrace = thisAgent->explain_backtrace_list;
175  thisAgent->explain_backtrace_list = back;
176 /* AGR 564 In last 2 statements, current_agent(...) was added. 2-May-94 */
177 
178 }
179 
180 /***************************************************************************
181 * Function : explain_add_temp_to_chunk_list
182 * Description : Allocate a new chunk structure and copy the information in
183 * the temp structure to it. Also copy in the current
184 * "explain_backtrace_list" and reset that list.
185 * We want to copy all the information in the chunk/justification
186 * in case it is excised or retracted later on and you still
187 * want an explanation. Therefore each item used is carefully
188 * copied, rather than just keeping a pointer.
189 **************************************************************************/
190 
192 
193 explain_chunk_str *chunk;
194 
195  chunk = static_cast<explain_chunk_str *>(malloc(sizeof (explain_chunk_str)));
196  chunk->conds = temp->conds;
197  chunk->actions = temp->actions;
198  strncpy(chunk->name,temp->name,EXPLAIN_CHUNK_STRUCT_NAME_BUFFER_SIZE);
200 
201  chunk->backtrace = thisAgent->explain_backtrace_list;
202  thisAgent->explain_backtrace_list = NULL;
203 /* AGR 564 In last 2 statements, current_agent(...) was added. 2-May-94 */
204 
205  chunk->all_grounds = copy_cond_list(thisAgent, temp->all_grounds);
206 
207  chunk->next_chunk = thisAgent->explain_chunk_list;
208  thisAgent->explain_chunk_list = chunk;
209 /* AGR 564 In last 2 statements, current_agent(...) was added. 2-May-94 */
210 
211 }
212 
213 
214 /***************************************************************************
215  * Function : free_explain_chunk
216  **************************************************************************/
217 
218 /* Note - the calling procedure must ensure that the list which "chunk" is
219  a part of is correctly updated to allow for its removal. */
220 
221 void free_explain_chunk(agent* thisAgent, explain_chunk_str *chunk) {
222 
223  /* First free up all the traced productions */
224  free_backtrace_list(thisAgent, chunk->backtrace);
225 
226  deallocate_condition_list(thisAgent, chunk->conds);
227  deallocate_action_list(thisAgent, chunk->actions);
228  deallocate_condition_list(thisAgent, chunk->all_grounds);
229 
230  /* Then free up this structure */
231  free(chunk);
232 }
233 
234 /***************************************************************************
235  * Function : reset_explain
236  **************************************************************************/
237 
238 void reset_explain (agent* thisAgent) {
239 
240  explain_chunk_str *top, *chunk;
241 
242  top = thisAgent->explain_chunk_list;
243 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
244 
245  while (top != NULL) {
246  chunk = top;
247  top = top->next_chunk;
248  free_explain_chunk(thisAgent, chunk);
249  }
250 
251  thisAgent->explain_chunk_list = NULL;
252 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
253 
254  reset_backtrace_list(thisAgent);
255 }
256 
257 
258 /***************************************************************************
259  * Function : find_chunk
260  * Description : Find the data structure associated with an explain chunk by
261  * searching for its name.
262  **************************************************************************/
263 
264 explain_chunk_str *find_chunk(agent* thisAgent, explain_chunk_str *chunk, char *name) {
265 
266  while (chunk != NULL) {
267  if (strcmp(chunk->name,name) == 0)
268  return(chunk);
269  chunk = chunk->next_chunk;
270  }
271 
272  print(thisAgent, "Could not find the chunk. Maybe explain was not on when it was created.");
273  /* BUGBUG: this doesn't belong here! changed for bug 608 */
274  print (thisAgent, "\nTo turn on explain: save-backtraces --enable before the chunk is created.\n");
275 
276  return (NULL);
277 }
278 
279 
280 /***************************************************************************
281  * Function : find_ground
282  * Description : Find the numbered condition in the chunk.
283  **************************************************************************/
284 
285 condition *find_ground(agent* thisAgent, explain_chunk_str *chunk, int number) {
286 
287  condition *ground, *cond;
288 
289  ground = NIL; /* unnecessary, but gcc -Wall warns without it */
290  for (cond = chunk->all_grounds; cond != NIL; cond = cond->next) {
291  number--;
292  if (number == 0)
293  ground = cond;
294  }
295  if (number > 0) {
296  print(thisAgent, "Could not find this condition.\n");
297  return (NIL);
298  }
299  return (ground);
300 }
301 
302 /***************************************************************************
303  * Function : explain_trace_chunk
304  **************************************************************************/
305 
306 void explain_trace_chunk(agent* thisAgent, explain_chunk_str *chunk) {
307 
308 backtrace_str *prod;
309 
310  print(thisAgent, "Chunk : %s\n",chunk->name);
311  prod = chunk->backtrace;
312  while (prod != NULL) {
313  print(thisAgent, "Backtrace production : %s\n",prod->prod_name);
314  print(thisAgent, "Result : %d\n",prod->result);
315  if (prod->trace_cond != NULL) {
316  print(thisAgent, "Trace condition : ");
317  print_condition(thisAgent, prod->trace_cond);
318  }
319  else
320  print(thisAgent, "The result preference is not stored, sorry.\n");
321  print_string (thisAgent, "\nGrounds:\n");
322  print_list_of_conditions (thisAgent, prod->grounds);
323  print_string (thisAgent, "\nPotentials:\n");
324  print_list_of_conditions (thisAgent, prod->potentials);
325  print_string (thisAgent, "\nLocals:\n");
326  print_list_of_conditions (thisAgent, prod->locals);
327  print_string (thisAgent, "\nNegateds:\n");
328  print_list_of_conditions (thisAgent, prod->negated);
329  prod = prod -> next_backtrace;
330  print(thisAgent, "\n\n");
331  }
332 }
333 
334 /***************************************************************************
335  * Function : explain_trace_named_chunk
336  **************************************************************************/
337 
338 void explain_trace_named_chunk(agent* thisAgent, char *chunk_name) {
339 
340 explain_chunk_str *chunk;
341 
342  chunk = find_chunk(thisAgent, thisAgent->explain_chunk_list,chunk_name);
343 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
344 
345  if (chunk)
346  explain_trace_chunk(thisAgent, chunk);
347 }
348 
349 /***************************************************************************
350  * Function : explain_find_cond
351  * Description : Return the matching condition from the list, NULL if no match.
352  **************************************************************************/
353 
355 
356 condition *cond, *match;
357 
358  match = NULL;
359  for (cond=cond_list; cond!=NULL; cond=cond->next) {
360  if (conditions_are_equal (target,cond))
361  match = cond;
362  }
363  return (match);
364 }
365 
366 /***************************************************************************
367  * Function : explain_trace
368  * Description : Search the backtrace structures to explain why the given
369  * condition appeared in the chunk.
370  **************************************************************************/
371 
372 void explain_trace(agent* thisAgent, char *chunk_name, backtrace_str *prod_list, condition *ground) {
373 
374 int count;
375 condition *match, *target;
376 backtrace_str *prod;
377 
378  /* Find which prod. inst. tested the ground originally to get
379  it included in the chunk.
380  Need to check potentials too, in case they got included
381  later on. */
382 
383  prod = prod_list;
384  match = NULL;
385  while (prod != NULL && match == NULL)
386  {
387  match = explain_find_cond(ground,prod->potentials);
388  if (match == NULL) match = explain_find_cond(ground,prod->grounds);
389  if (match == NULL) match = explain_find_cond(ground,prod->negated);
390  if (match == NULL) prod = prod->next_backtrace;
391  }
392 
393  if (match == NULL) {
394  print(thisAgent, "EXPLAIN: Error, couldn't find the ground condition\n");
395  return;
396  }
397 
398  print(thisAgent, "Explanation of why condition ");
399  print_condition(thisAgent, ground);
400  print(thisAgent, " was included in %s\n\n",chunk_name);
401 
402  print(thisAgent, "Production %s matched\n ",prod->prod_name);
403  print_condition(thisAgent, match);
404  print(thisAgent, " which caused\n");
405 
406  /* Trace back the series of productions to find which one
407  caused the matched condition to be created.
408  Build in a safety limit of tracing 50 productions before cancelling.
409  This is in case there is a loop in the search procedure somehow or
410  a really long sequence of production firings. Either way you probably
411  don't want to see more than 50 lines of junk.... */
412 
413  target = prod->trace_cond;
414  count = 0;
415 
416  while (prod->result == FALSE && count < 50 && match != NULL) {
417  prod = prod_list;
418  match = NULL;
419  count++;
420  while (prod != NULL && match == NULL) {
421  match = explain_find_cond(target,prod->locals);
422  /* Going to check all the other lists too just to be sure */
423  if (match == NULL) match = explain_find_cond(target,prod->negated);
424  if (match == NULL) match = explain_find_cond(target,prod->potentials);
425  if (match == NULL) match = explain_find_cond(target,prod->grounds);
426  if (match == NULL) prod = prod->next_backtrace;
427  }
428 
429  if (match == NULL) {
430  print(thisAgent, "EXPLAIN : Unable to find which production matched condition ");
431  print_condition(thisAgent, target);
432  print(thisAgent, "\nTo help understand what happened here and help debug this\n");
433  print(thisAgent, "here is all of the backtracing information stored for this chunk.\n");
434  print(thisAgent, "\n");
435  explain_trace_named_chunk(thisAgent, chunk_name);
436  }
437  else {
438  print(thisAgent, "production %s to match\n ",prod->prod_name);
439  print_condition(thisAgent, match);
440  print(thisAgent, " which caused\n");
441  target = prod->trace_cond;
442  }
443  }
444 
445  if (prod->result == TRUE)
446  print(thisAgent, "A result to be generated.\n");
447  if (count >= 50)
448  print(thisAgent, "EXPLAIN: Exceeded 50 productions traced through, so terminating now.\n");
449 }
450 
451 /***************************************************************************
452  * Function : explain_chunk
453  * Description : Explain why the numbered condition appears in the given chunk.
454  **************************************************************************/
455 
456 void explain_chunk(agent* thisAgent, char *chunk_name, int cond_number) {
457 
458 explain_chunk_str *chunk;
459 condition *ground;
460 
461  chunk = find_chunk(thisAgent, thisAgent->explain_chunk_list,chunk_name);
462 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
463 
464  if (chunk == NULL) return;
465 
466  ground = find_ground(thisAgent, chunk,cond_number);
467  if (ground == NIL) return;
468 
469  explain_trace(thisAgent, chunk_name,chunk->backtrace,ground);
470 }
471 
472 /***************************************************************************
473  * Function : explain_cond_list
474  * Description : List all of the conditions and number them for a named chunk.
475  **************************************************************************/
476 
477 void explain_cond_list(agent* thisAgent, char *chunk_name) {
478 
479 explain_chunk_str *chunk;
480 condition *cond, *ground;
481 int i;
482 
483  chunk = find_chunk(thisAgent, thisAgent->explain_chunk_list,chunk_name);
484 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
485 
486  if (chunk == NULL) return;
487 
488  /* First print out the production in "normal" form */
489 
490  print (thisAgent, "(sp %s\n ", chunk->name);
491  print_condition_list (thisAgent, chunk->conds, 2, FALSE);
492  print (thisAgent, "\n-->\n ");
493  print_action_list (thisAgent, chunk->actions, 3, FALSE);
494  print(thisAgent, ")\n\n");
495 
496  /* Then list each condition and the associated "ground" WME */
497 
498  i = 0;
499  ground = chunk->all_grounds;
500 
501  for (cond = chunk->conds; cond != NIL; cond = cond->next) {
502  i++; print(thisAgent, " %2d : ",i);
503  print_condition(thisAgent, cond);
504  while (get_printer_output_column(thisAgent) < COLUMNS_PER_LINE-40)
505  print(thisAgent, " ");
506 
507  print(thisAgent, " Ground :");
508  print_condition(thisAgent, ground);
509  print(thisAgent, "\n");
510  ground=ground->next;
511  }
512 }
513 
514 
515 /***************************************************************************
516  * Function : explain_list_chunks
517  **************************************************************************/
518 
519 void explain_list_chunks (agent* thisAgent) {
520 
521 explain_chunk_str *chunk;
522 
523  chunk = thisAgent->explain_chunk_list;
524 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
525 
526  if (!chunk)
527  print (thisAgent, "No chunks/justifications built yet!\n");
528  else {
529  print(thisAgent, "List of all explained chunks/justifications:\n");
530  while (chunk != NULL) {
531  print(thisAgent, "Have explanation for %s\n",chunk->name);
532  chunk = chunk->next_chunk;
533  }
534  }
535 }
536 
537 /***************************************************************************
538  * Function : explain_full_trace
539  **************************************************************************/
540 
541 void explain_full_trace (agent* thisAgent) {
542 
543 explain_chunk_str *chunk;
544 
545  chunk = thisAgent->explain_chunk_list;
546 /* AGR 564 In previous statement, current_agent(...) was added. 2-May-94 */
547 
548  while (chunk != NULL) {
549  explain_trace_chunk(thisAgent, chunk);
550  chunk = chunk->next_chunk;
551  }
552 }
553