Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

explain.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  explain.c
00004  *
00005  * =======================================================================
00006  * Description  :  To provide a function which at the least can do :
00007  *                  (explain chunk-1)
00008  *                lists conditions -- given one can list the productions
00009  *                which fired & caused it to be in the chunk.
00010  *
00011  *                (It would only run AFTER backtracing).
00012  * =======================================================================
00013  *
00014  * Copyright 1995-2003 Carnegie Mellon University,
00015  *                                                                               University of Michigan,
00016  *                                                                               University of Southern California/Information
00017  *                                                                               Sciences Institute. All rights reserved.
00018  *                                                                              
00019  * Redistribution and use in source and binary forms, with or without
00020  * modification, are permitted provided that the following conditions are met:
00021  *
00022  * 1.   Redistributions of source code must retain the above copyright notice,
00023  *              this list of conditions and the following disclaimer. 
00024  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00025  *              this list of conditions and the following disclaimer in the documentation
00026  *              and/or other materials provided with the distribution. 
00027  *
00028  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00029  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00030  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00031  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00032  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00033  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00034  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00035  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00036  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00037  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00038  * The views and conclusions contained in the software and documentation are
00039  * those of the authors and should not be interpreted as representing official
00040  * policies, either expressed or implied, of Carnegie Mellon University, the
00041  * University of Michigan, the University of Southern California/Information
00042  * Sciences Institute, or the Soar consortium.
00043  * =======================================================================
00044  */
00045 
00046 /*
00047   NOTES :
00048      1)  Explain search just finds ANY path--should be shortest.
00049 */
00050 
00051 #include "soarkernel.h"         /* Condition type defs */
00052 #include "soar_ecore_api.h"
00053 #include "explain.h"
00054 
00055 /* Define the "local" globals if that makes sense.   
00056    (Only accessed in this file) 
00057    The backtrace_list is built up until a call is made to create a new    
00058    entry in the chunk_list.  At that time the current backtrace list is   
00059    included in that structure and a new backtrace list begun.           */
00060 
00061 /* static explain_chunk_str *explain_chunk_list;
00062    static backtrace_str     *explain_backtrace_list;
00063    static char explain_chunk_name[256] = { '\0' };
00064    AGR 564 */
00065 
00066 /* AGR 564  This bug report came complete with fixes from Frank Koss.
00067    So, I just implemented the fixes.  The files with changes in them
00068    for this bug are explain.c, explain.h, 
00069    init_soar.c, interface.c, and soarkernel.h.  AGR 564 2-May-94  */
00070 
00071 /***************************************************************************
00072  * Function     : init_explain
00073  **************************************************************************/
00074 
00075 void init_explain(void)
00076 {
00077 
00078 /* "AGR 564" applies to this whole function */
00079 
00080     current_agent(explain_chunk_name[0]) = '\0';
00081     current_agent(explain_chunk_list) = NULL;
00082     current_agent(explain_backtrace_list) = NULL;
00083     /* added in this initialization, not sure why removed...  KJC 7/96 */
00084     /* current_agent(explain_flag) = FALSE;
00085      */
00086     /*  should we be re-initializing here??  */
00087     set_sysparam(EXPLAIN_SYSPARAM, FALSE);
00088 
00089 /*
00090  * add_help("explain",help_on_explain);
00091  * add_command("explain",explain_interface_routine);
00092  *
00093  * explain_chunk_list = NULL;
00094  * explain_backtrace_list = NULL;
00095  * current_agent(explain_flag) = FALSE;
00096  */
00097 }
00098 
00099 /***************************************************************************
00100  * Function     : free_backtrace_list
00101  **************************************************************************/
00102 
00103 void free_backtrace_list(backtrace_str * prod)
00104 {
00105 
00106     backtrace_str *next_prod;
00107 
00108     while (prod != NULL) {
00109         next_prod = prod->next_backtrace;
00110         deallocate_condition_list(prod->trace_cond);
00111         deallocate_condition_list(prod->grounds);
00112         deallocate_condition_list(prod->potentials);
00113         deallocate_condition_list(prod->locals);
00114         deallocate_condition_list(prod->negated);
00115         free((void *) prod);
00116         prod = next_prod;
00117     }
00118 }
00119 
00120 /***************************************************************************
00121  * Function     : reset_backtrace_list
00122  **************************************************************************/
00123 
00124 void reset_backtrace_list(void)
00125 {
00126 
00127     free_backtrace_list(current_agent(explain_backtrace_list));
00128     current_agent(explain_backtrace_list) = NULL;
00129 /* AGR 564  In both statements, the current_agent(...) was added.  2-May-94 */
00130 
00131 }
00132 
00133 /***************************************************************************
00134  * Function     : copy_cond_list
00135  **************************************************************************/
00136 
00137 condition *copy_cond_list(condition * top_list)
00138 {
00139 
00140     condition *new_top, *new_bottom;
00141 
00142     copy_condition_list(top_list, &new_top, &new_bottom);
00143     return (new_top);
00144 }
00145 
00146 /***************************************************************************
00147  * Function     : copy_conds_from_list
00148  **************************************************************************/
00149 
00150 condition *copy_conds_from_list(cons * top_list)
00151 {
00152 
00153     condition *top, *cond, *prev, *next;
00154     cons *cc;
00155 
00156     prev = next = top = NIL;
00157 
00158     for (cc = top_list; cc != NIL; cc = cc->rest) {
00159         cond = copy_condition(cc->first);
00160         cond->prev = prev;
00161         cond->next = NIL;
00162 
00163         if (prev == NIL)
00164             top = cond;
00165         else
00166             prev->next = cond;
00167 
00168         prev = cond;
00169     }
00170     return (top);
00171 }
00172 
00173 /***************************************************************************
00174  * Function     : explain_add_temp_to_backtrace_list
00175  **************************************************************************/
00176 
00177 void explain_add_temp_to_backtrace_list
00178     (backtrace_str * temp, cons * grounds, cons * pots, cons * locals, cons * negateds) {
00179 
00180     backtrace_str *back;
00181 
00182     back = (backtrace_str *) malloc(sizeof(backtrace_str));
00183     back->result = temp->result;
00184     back->trace_cond = copy_condition(temp->trace_cond);
00185     if (back->trace_cond != NULL)
00186         back->trace_cond->next = NULL;
00187     strncpy(back->prod_name, temp->prod_name, PROD_NAME_SIZE);
00188     back->prod_name[PROD_NAME_SIZE - 1] = 0;
00189 
00190     back->grounds = copy_conds_from_list(grounds);
00191     back->potentials = copy_conds_from_list(pots);
00192     back->locals = copy_conds_from_list(locals);
00193     back->negated = copy_conds_from_list(negateds);
00194 
00195     back->next_backtrace = current_agent(explain_backtrace_list);
00196     current_agent(explain_backtrace_list) = back;
00197 /* AGR 564  In last 2 statements, current_agent(...) was added.  2-May-94 */
00198 
00199 }
00200 
00201 /***************************************************************************
00202 * Function     : explain_add_temp_to_chunk_list
00203 * Description  : Allocate a new chunk structure and copy the information in
00204 *                the temp structure to it.  Also copy in the current
00205 *                "explain_backtrace_list" and reset that list.
00206 *                We want to copy all the information in the chunk/justification
00207 *                in case it is excised or retracted later on and you still
00208 *                want an explanation.  Therefore each item used is carefully
00209 *                copied, rather than just keeping a pointer.
00210 **************************************************************************/
00211 
00212 void explain_add_temp_to_chunk_list(explain_chunk_str * temp)
00213 {
00214 
00215     explain_chunk_str *chunk;
00216 
00217     chunk = (explain_chunk_str *) malloc(sizeof(explain_chunk_str));
00218     chunk->conds = temp->conds;
00219     chunk->actions = temp->actions;
00220     strncpy(chunk->name, temp->name, PROD_NAME_SIZE);
00221     chunk->name[PROD_NAME_SIZE - 1] = 0;
00222 
00223     chunk->backtrace = current_agent(explain_backtrace_list);
00224     current_agent(explain_backtrace_list) = NULL;
00225 /* AGR 564  In last 2 statements, current_agent(...) was added.  2-May-94 */
00226 
00227     chunk->all_grounds = copy_cond_list(temp->all_grounds);
00228 
00229     chunk->next_chunk = current_agent(explain_chunk_list);
00230     current_agent(explain_chunk_list) = chunk;
00231 /* AGR 564  In last 2 statements, current_agent(...) was added.  2-May-94 */
00232 
00233 }
00234 
00235 /***************************************************************************
00236  * Function     : free_explain_chunk
00237  **************************************************************************/
00238 
00239 /* Note - the calling procedure must ensure that the list which "chunk" is   
00240           a part of is correctly updated to allow for its removal.         */
00241 
00242 void free_explain_chunk(explain_chunk_str * chunk)
00243 {
00244 
00245     /* First free up all the traced productions */
00246     free_backtrace_list(chunk->backtrace);
00247 
00248     deallocate_condition_list(chunk->conds);
00249     deallocate_action_list(chunk->actions);
00250     deallocate_condition_list(chunk->all_grounds);
00251 
00252     /* Then free up this structure */
00253     free((void *) chunk);
00254 }
00255 
00256 /***************************************************************************
00257  * Function     : reset_explain
00258  **************************************************************************/
00259 
00260 void reset_explain(void)
00261 {
00262 
00263     explain_chunk_str *top, *chunk;
00264 
00265     top = current_agent(explain_chunk_list);
00266 /* AGR 564  In previous statement, current_agent(...) was added.  2-May-94 */
00267 
00268     while (top != NULL) {
00269         chunk = top;
00270         top = top->next_chunk;
00271         free_explain_chunk(chunk);
00272     }
00273 
00274     current_agent(explain_chunk_list) = NULL;
00275 /* AGR 564  In previous statement, current_agent(...) was added.  2-May-94 */
00276 
00277     reset_backtrace_list();
00278 }
00279 
00280 /***************************************************************************
00281  * Function     : find_chunk
00282  * Description  : Find the data structure associated with an explain chunk by
00283  *                searching for its name.
00284  **************************************************************************/
00285 
00286 explain_chunk_str *find_chunk(explain_chunk_str * chunk, char *name)
00287 {
00288 
00289     while (chunk != NULL) {
00290         if (strcmp(chunk->name, name) == 0)
00291             return (chunk);
00292         chunk = chunk->next_chunk;
00293     }
00294 
00295     print("Could not find the chunk.  Maybe explain was not on when it was created.");
00296     /* BUGBUG *** shouldn't have user interface stuff in kernel!! */
00297     print("\nFor Soar 7: set save_backtraces 1 before the chunk is created.\n");
00298 
00299     return (NULL);
00300 }
00301 
00302 /***************************************************************************
00303  * Function     : find_ground
00304  * Description  : Find the numbered condition in the chunk.
00305  **************************************************************************/
00306 
00307 condition *find_ground(explain_chunk_str * chunk, int number)
00308 {
00309 
00310     condition *ground, *cond;
00311 
00312     ground = NIL;               /* unnecessary, but gcc -Wall warns without it */
00313     for (cond = chunk->all_grounds; cond != NIL; cond = cond->next) {
00314         number--;
00315         if (number == 0)
00316             ground = cond;
00317     }
00318     if (number > 0) {
00319         print("Could not find this condition.\n");
00320         return (NIL);
00321     }
00322     return (ground);
00323 }
00324 
00325 /***************************************************************************
00326  * Function     : explain_trace_chunk
00327  **************************************************************************/
00328 
00329 void explain_trace_chunk(explain_chunk_str * chunk)
00330 {
00331 
00332     backtrace_str *prod;
00333 
00334     print("Chunk : %s\n", chunk->name);
00335     prod = chunk->backtrace;
00336     while (prod != NULL) {
00337         print("Backtrace production : %s\n", prod->prod_name);
00338         print("Result : %d\n", prod->result);
00339         if (prod->trace_cond != NULL) {
00340             print("Trace condition : ");
00341             print_condition(prod->trace_cond);
00342         } else
00343             print("The result preference is not stored, sorry.\n");
00344         print_string("\nGrounds:\n");
00345         print_list_of_conditions(prod->grounds);
00346         print_string("\nPotentials:\n");
00347         print_list_of_conditions(prod->potentials);
00348         print_string("\nLocals:\n");
00349         print_list_of_conditions(prod->locals);
00350         print_string("\nNegateds:\n");
00351         print_list_of_conditions(prod->negated);
00352         prod = prod->next_backtrace;
00353         print("\n\n");
00354     }
00355 }
00356 
00357 /***************************************************************************
00358  * Function     : explain_find_cond
00359  * Description  : Return the matching condition from the list, NULL if no match.
00360  **************************************************************************/
00361 
00362 condition *explain_find_cond(condition * target, condition * cond_list)
00363 {
00364 
00365     condition *cond, *match;
00366 
00367     match = NULL;
00368     for (cond = cond_list; cond != NULL; cond = cond->next) {
00369         if (conditions_are_equal(target, cond))
00370             match = cond;
00371     }
00372     return (match);
00373 }
00374 
00375 /***************************************************************************
00376  * Function     : explain_trace
00377  * Description  : Search the backtrace structures to explain why the given
00378  *                condition appeared in the chunk.
00379  **************************************************************************/
00380 
00381 void explain_trace(char *chunk_name, backtrace_str * prod_list, condition * ground)
00382 {
00383 
00384     int count;
00385     condition *match, *target;
00386     backtrace_str *prod;
00387 
00388     /* Find which prod. inst. tested the ground originally to get   
00389        it included in the chunk.                                    
00390        Need to check potentials too, in case they got included      
00391        later on.                                                  */
00392 
00393     prod = prod_list;
00394     match = NULL;
00395     while (prod != NULL && match == NULL) {
00396         match = explain_find_cond(ground, prod->potentials);
00397         if (match == NULL)
00398             match = explain_find_cond(ground, prod->grounds);
00399         if (match == NULL)
00400             match = explain_find_cond(ground, prod->negated);
00401         if (match == NULL)
00402             prod = prod->next_backtrace;
00403     }
00404 
00405     if (match == NULL) {
00406         print("EXPLAIN: Error, couldn't find the ground condition\n");
00407         return;
00408     }
00409 
00410     print("Explanation of why condition ");
00411     print_condition(ground);
00412     print(" was included in %s\n\n", chunk_name);
00413 
00414     print("Production %s matched\n   ", prod->prod_name);
00415     print_condition(match);
00416     print(" which caused\n");
00417 
00418     /* Trace back the series of productions to find which one                   
00419        caused the matched condition to be created.                              
00420        Build in a safety limit of tracing 50 productions before cancelling.     
00421        This is in case there is a loop in the search procedure somehow or       
00422        a really long sequence of production firings.  Either way you probably   
00423        don't want to see more than 50 lines of junk....                       */
00424 
00425     target = prod->trace_cond;
00426     count = 0;
00427 
00428     while (prod->result == FALSE && count < 50 && match != NULL) {
00429         prod = prod_list;
00430         match = NULL;
00431         count++;
00432         while (prod != NULL && match == NULL) {
00433             match = explain_find_cond(target, prod->locals);
00434             /* Going to check all the other lists too just to be sure */
00435             if (match == NULL)
00436                 match = explain_find_cond(target, prod->negated);
00437             if (match == NULL)
00438                 match = explain_find_cond(target, prod->potentials);
00439             if (match == NULL)
00440                 match = explain_find_cond(target, prod->grounds);
00441             if (match == NULL)
00442                 prod = prod->next_backtrace;
00443         }
00444 
00445         if (match == NULL) {
00446             print("EXPLAIN : Unable to find which production matched condition ");
00447             print_condition(target);
00448             print("\nTo help understand what happened here and help debug this\n");
00449             print("here is all of the backtracing information stored for this chunk.\n");
00450             print("\n");
00451             soar_ecExplainChunkTrace(chunk_name);
00452         } else {
00453             print("production %s to match\n   ", prod->prod_name);
00454             print_condition(match);
00455             print(" which caused\n");
00456             target = prod->trace_cond;
00457         }
00458     }
00459 
00460     if (prod->result == TRUE)
00461         print("A result to be generated.\n");
00462     if (count >= 50)
00463         print("EXPLAIN: Exceeded 50 productions traced through, so terminating now.\n");
00464 }
00465 
00466 /***************************************************************************
00467  * Function     : explain_list_chunks
00468  **************************************************************************/
00469 
00470 void explain_list_chunks(void)
00471 {
00472 
00473     explain_chunk_str *chunk;
00474 
00475     chunk = current_agent(explain_chunk_list);
00476 /* AGR 564  In previous statement, current_agent(...) was added.  2-May-94 */
00477 
00478     if (!chunk)
00479         print("No chunks/justifications built yet!\n");
00480     else {
00481         print("List of all explained chunks/justifications:\n");
00482         while (chunk != NULL) {
00483             print("Have explanation for %s\n", chunk->name);
00484             chunk = chunk->next_chunk;
00485         }
00486     }
00487 }
00488 
00489 /***************************************************************************
00490  * Function     : explain_full_trace
00491  **************************************************************************/
00492 
00493 void explain_full_trace(void)
00494 {
00495 
00496     explain_chunk_str *chunk;
00497 
00498     chunk = current_agent(explain_chunk_list);
00499 /* AGR 564  In previous statement, current_agent(...) was added.  2-May-94 */
00500 
00501     while (chunk != NULL) {
00502         explain_trace_chunk(chunk);
00503         chunk = chunk->next_chunk;
00504     }
00505 }

Generated on Thu Dec 11 13:00:16 2003 for Soar Kernel by doxygen 1.3.5